# SDD: IOP Agent CLI Runtime

## 위치

- Milestone: [IOP Agent CLI Runtime](../../../phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md)
- Phase: [PHASE.md](../../../../phase/automation-runtime-bridge/PHASE.md)

## 상태

[승인됨]

## SDD 잠금

- 상태: 해제
- 사용자 리뷰: [user_review_0.log](user_review_0.log), [user_review_1.log](user_review_1.log)
- 잠금 항목: 없음

## 문제 / 비목표

- 문제: Agent Task 실행·관측·복구 책임이 현재 Python dispatcher를 모델이 감시하는 흐름과 Node 내부 CLI runtime에 나뉘어 있다. 또한 COW/worktree/clone 격리가 적용되기 전의 project shared checkout에서는 기능상 독립인 PLAN도 같은 파일을 서로 다른 시각에 수정·검증해 상대 작업 때문에 build/test가 실패할 수 있다. 이 SDD는 검증된 동작을 축소하지 않고 공통 Go runtime과 개인 장비의 단일 `iop-agent` CLI로 이전하면서 등록 workspace 안의 전자동 실행 guardrail, shared-checkout write claim, 다중 project, Flutter·Unity subprocess, Node가 같은 provider·manager 구현을 소비하는 책임, lifecycle, 상태와 evidence 경계를 고정한다.
- 비목표:
  - Flutter 설정 UI, tray, macOS `.app` shell과 Unity 3D Character를 구현하지 않는다.
  - Python 코드를 production dependency나 fallback으로 사용하거나 진행 중 Python process state를 승계하지 않는다.
  - provider 로그인, credential 저장, 사용자 승인 UI와 billing 자동화를 구현하지 않는다.
  - agent-ops를 사용하지 않는 일반 요청의 direct/Plan/Milestone 분류나 합성 tool call 주입을 구현하지 않는다.
  - local proto-socket의 세부 field를 SDD에 계약 원문으로 복제하지 않는다.

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | [IOP Agent CLI Runtime](../../../phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md) | CLI 목표, 기능 Task, 범위와 완료 상태의 원본 |
| 이전 설계 | [공통 Agent Task Runtime과 Desktop Agent](../../../phase/automation-runtime-bridge/milestones/shared-agent-task-runtime-desktop-agent.md), [기존 SDD](../../../sdd/automation-runtime-bridge/shared-agent-task-runtime-desktop-agent/SDD.md) | CLI parity 요구를 이관할 참조이며 결합된 Desktop delivery는 구현 입력이 아님 |
| Node Wire | [Edge-Node Runtime Wire](../../../../../agent-contract/inner/edge-node-runtime-wire.md) | Node bridge가 보존해야 할 기존 `RunRequest`/`RunEvent`, cancel, command와 config 의미 |
| Config Compatibility | [Edge Config Runtime Refresh](../../../../../agent-contract/inner/edge-config-runtime-refresh.md) | 기존 Node provider/config 의미의 호환 기준이며 `iop-agent` repo-global/local config 원문을 대신하지 않음 |
| Project Workflow | 등록 project의 agent-ops Milestone·Plan·Code Review·USER_REVIEW 계약과 workflow adapter | 작업 의미와 artifact contract는 project가 소유하고 runtime은 구조 판정과 실행을 소유함 |
| Project State | 각 workspace의 `agent-task`, `agent-roadmap`, `WORK_LOG.md`, `agent-log` | 작업 원문, 진행, review와 완료 evidence의 durable source of truth |
| PLAN Write Set | active PLAN의 정확히 하나인 `Modified Files Summary` 표 | 첫 번째 `File` column의 backtick file path를 정규화한 집합이 shared-checkout claim 입력이며 LLM 해석이나 추정 target을 사용하지 않음 |
| Shared-checkout Claim State | workspace Git metadata 아래 dispatcher-owned ledger | orchestration/task group별로 분리하지 않고 같은 physical checkout의 모든 active task가 공유하는 task/PLAN revision, canonical workspace identity, normalized file key, owner PID/start token·locator와 claim lifecycle의 runtime source of truth이며 project 문서나 roadmap에는 mutable claim을 기록하지 않음 |
| Repo-global Config | 등록 project repo의 versioned YAML | 비밀정보 없는 provider/default/selection policy template의 source of truth이며 runtime은 읽기만 함 |
| User-local State | 소유 OS 사용자의 local config/state root | project registry·canonical workspace grant·장비 경로·provider 실행 참조·project override·자동 재개·client launch 설정과 versioned checkpoint/lease의 source of truth |
| External Provider | 사용자가 YAML에 선언하고 이미 인증한 CLI provider | runtime은 discovery, status, unattended/approval-bypass capability, 실행과 cancel만 확인하며 인증을 소유하지 않음 |
| User Decision | 2026-07-28 실행·설정·병렬화·process topology·approval 결정과 2026-07-29 shared-checkout file-lock 정책 | D01~D05는 [user_review_0.log](user_review_0.log), task overlay·직렬 통합 D06은 [user_review_1.log](user_review_1.log)에 확정했고 shared-checkout claim은 이 SDD의 interface와 S20에 반영함 |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| `starting` | `iop-agent serve`가 소유 OS 사용자의 device singleton lease를 요청했거나 Node host가 공통 library를 시작했다 | `config-validating` 또는 `failed` | runtime build, host identity와 singleton lease |
| `config-validating` | repo-global read-only YAML과 user-local config/override revision을 읽었다 | `provider-discovering`, `config-error` | schema validation, merge precedence와 immutable config revision |
| `provider-discovering` | 선언 provider의 binary/version/authenticated readiness/status와 unattended/approval-bypass capability를 조회한다 | `project-watching`, `provider-error` | provider discovery snapshot |
| `project-watching` | 명시 등록 workspace와 config watcher가 활성화됐다 | `workspace-claiming`, `idle`, `stopped` | registry와 filesystem event |
| `workspace-claiming` | 사용자가 Milestone 최초 시작을 요청했거나 시작 기록이 있는 중단 작업의 manual/automatic resume가 요청됐다 | `guardrail-validating`, `blocked` | canonical workspace identity, start intent, lease와 checkpoint revision |
| `guardrail-validating` | workspace lease를 획득하고 dispatch 후보 provider/profile이 정해졌다 | `reconciling`, `blocked` | canonical root와 symlink containment, task writable-root confinement, 명시 VCS metadata allowance, unattended/approval-bypass capability와 immutable grant/config revision |
| `reconciling` | lease 획득 또는 host restart 뒤 filesystem, checkpoint, process/session, overlay/change set, integration queue와 completion ledger를 대조한다 | `idle`, `work-ready`, `running`, `integration-waiting`, `integrating`, `blocked`, `failed` | execution/attempt, process/overlay locator, active pair, IntegrationRecord, archive와 last-writer state |
| `idle` | ready work가 없고 watcher가 활성 상태다 | `work-ready`, `config-pending`, `stopped` | project scan과 watcher event |
| `work-ready` | 수동 시작된 Milestone에 남은 dependency-ready task 또는 기존 overlay의 pinned resume/selfcheck/review stage가 있다 | `previewing`, 새 task의 `overlay-preparing`, 기존 overlay의 `selecting` 또는 `running`, `stopped` | explicit predecessor, provider concurrency, overlay identity와 persisted route state |
| `previewing` | read-only preview가 요청됐다 | 영속 전이 없이 caller에 반환 | 동일 selector/dependency 판정과 no-side-effect evidence |
| `overlay-preparing` | dependency-ready task에 dispatch ordinal을 부여했다 | `selecting`, `running`, `blocked` | tracked·untracked·dirty content와 mode/symlink를 포함한 pinned base fingerprint, task writable layer, task-local temp/cache와 격리 mode |
| `selecting` | 새 worker stage에 config와 quota snapshot을 적용한다 | `running`, `blocked`, `selection-error` | 하나의 RouteDecision과 durable candidate/rule history |
| `running` | provider process/session이 pinned config·route와 task overlay view에서 실행 중이다 | `submission-validating`, `review-validating`, `retrying`, `failing-over`, `cancelling`, `blocked`, `failed` | normalized stream, process/session locator, overlay identity와 typed failure |
| `submission-validating` | worker 또는 selfcheck가 성공 종료했다 | pinned selfcheck의 `work-ready`, official review의 `work-ready`, Pi `evidence-repairing`, `retrying`, `blocked` | completing route, pair/identity와 provider-neutral artifact matcher |
| `evidence-repairing` | Pi selfcheck 뒤 review artifact의 worker-owned field가 미완성이고 같은 native context가 유효하다 | 같은 context의 `running`, official review의 `work-ready`, `blocked`, `cancelling` | durable repair intent, incomplete ordinal, locator와 matcher snapshot |
| `review-validating` | official review process가 종료했다 | PASS의 `change-set-validating`, WARN/FAIL의 `work-ready`, USER_REVIEW의 `blocked`, `retrying`, `failed` | exact verdict, task overlay의 filesystem progress, follow-up과 completion artifact |
| `change-set-validating` | official review PASS 뒤 task overlay를 immutable change set으로 동결했다 | `integration-waiting`, `blocked` | base fingerprint, additions/modifications/deletions, mode/symlink, write-set, contract/test/review evidence와 containment 검증 |
| `integration-waiting` | change set이 검증됐고 앞선 dispatch ordinal의 integrated 또는 terminal-deferred record를 기다린다 | `integrating`, `blocked`, `stopped` | deterministic first-attempt ordinal, retry attempt ordinal과 canonical base owner lease |
| `integrating` | 해당 workspace의 base mutation lease와 다음 integration attempt ordinal을 획득했다 | `reconciling`, `blocked`, `failed` | managed predecessor integration 또는 exact base fingerprint, atomic three-way apply, post-apply validation, rollback과 IntegrationRecord; blocker는 queue를 해제하고 해결된 change-set revision은 뒤에 새 attempt로 등록 |
| `retrying` | 알려진 same-target 복구 가능 오류와 stage budget이 남았다 | bounded backoff 뒤 `running`, `blocked`, `cancelling` | failure budget과 retry ordinal/deadline |
| `failing-over` | typed quota/context/model/stream failure와 unused eligible alternate가 있다 | fault-atomic route/context commit 뒤 `running`, `blocked` | persisted route history, runtime quota observation과 continuation handoff |
| `config-pending` | 실행 중 새 유효 config revision이 관측됐다 | 현재 실행 종료 뒤 `work-ready` 또는 `idle` | 실행 snapshot은 유지하고 다음 agent 호출부터 새 revision 적용 |
| `blocked` | invalid state, guardrail/preflight 실패, merge conflict, 검증 실패, 관리되지 않은 base drift, unknown error, budget 소진 또는 eligible target 부재로 work unit을 진행할 수 없다 | identity-matched resume/retry의 `guardrail-validating`, `integration-waiting` 또는 `reconciling`, 사용자 stop의 `stopped` | task-local blocker, 보존된 overlay/change set, project log와 actionable notification |
| `cancelling` | 사용자 또는 host가 project 실행 중단을 요청했다 | `stopped`, `failed` | process group/session cancel evidence |
| `stopped` | 명시 stop 또는 자동 재개 off 상태에서 중단 작업이 확인됐다 | `config-validating`, `project-watching`, `workspace-claiming` | 사용자 start/resume 또는 local config event |
| `failed` | unrecoverable runtime/config/provider 오류가 발생했다 | 독립 project는 계속되고 해당 project는 수정 후 `reconciling` | surfaced error와 보존된 route/checkpoint |

- client process lifecycle은 project 실행 상태와 직교한다. `iop-agent`가 Flutter·Unity별 `stopped → starting → connected → stopped/crashed`를 소유하고, crash auto-restart와 login launch 여부는 user-local 설정을 따르며 Unity의 상세 UI command는 Flutter `starting/connected`로 중계한다.
- 이 Milestone을 구현하는 project dispatcher의 shared-checkout safety sub-state는 제품 runtime의 dependency state와 분리한다. valid PLAN은 workspace 공통 ledger에서 `claim-waiting → claimed → active`로 전이하고 worker·selfcheck·official review 동안 같은 claim을 유지한다. review follow-up은 새 PLAN revision과 write-set으로 all-or-none `claim-transfer`한 뒤에만 재개한다. verified completion과 live owner 부재가 함께 확인되거나 명시적 reset이 task-owned mutation의 revert/격리/정리를 검증해야 `release-ready → released`가 된다. 교집합은 `claim-waiting`, 누락·중복·빈 값·glob·workspace 외부·디렉터리 target, 통제되지 않은 PLAN revision 변경 또는 restart ownership ambiguity는 invocation 없이 `claim-blocked`로 남긴다.

## Interface Contract

- 계약 원문:
  - Node 호환 경계는 [Edge-Node Runtime Wire](../../../../../agent-contract/inner/edge-node-runtime-wire.md)를 유지한다.
  - `iop-agent` repo-global/local config, workspace grant/guardrail, workspace snapshot·overlay·change-set integration과 local proto-socket의 client-neutral 상태·event·control·client process 계약은 현재 `agent-contract`에 없으므로 구현 계획의 첫 계약 작업에서 생성한다. 계약 생성 전 config/proto/isolation 코드를 확정하지 않는다.
- 입력:
  - `RuntimeConfig`: repo-global revision, user-local revision, provider catalog, merged defaults, selection policy, default isolation/fallback policy와 user-local overlay root·retention·log/state root다. user-local 값이 repo-global 뒤에 적용되고 ordered rule array는 전체 교체한다.
  - `ProjectRegistration`: stable registry id, canonical workspace instance, workspace grant reference, enabled, selected/started Milestone, `auto_resume_interrupted`와 project override다. ready Milestone의 자동 최초 시작은 허용하지 않는다.
  - `ProviderProfile`: stable provider/model/profile id, command/env reference, execution/session/status capability, unattended/approval-bypass mode, task-view compatibility와 iop-agent-owned writable-root confinement capability다.
  - `WorkspaceGrant`: 사용자가 등록한 canonical workspace root, symlink-resolved containment, default overlay mutation scope, full clone의 내부 `.git` 또는 worktree의 명시적 git common-dir metadata allowance와 immutable grant revision이다. 등록은 이 범위의 agent action을 사전 승인하되 task process의 canonical base 직접 쓰기를 허용하지 않는다.
  - `SelectionPolicy`: default target과 시간, quota/token, agent/stage/lane/grade, capability, known failure 조건을 가진 ordered rule array다.
  - `WorkRequest`: project/workspace, 사용자가 선택·시작했거나 재개 대상인 Milestone/task, stage/work-unit, explicit predecessor, declared write-set, `overlay | worktree | clone` isolation mode, dispatch ordinal과 persisted route identity다.
  - `PlanWriteSet`: active PLAN의 정확히 하나인 `Modified Files Summary` 첫 번째 column에서 읽은 하나 이상의 backtick file path다. dispatcher는 line suffix를 제거하고 symlink·case alias를 포함한 physical checkout 기준 file key로 정규화하며 glob, workspace root·directory와 containment 밖 경로를 거부한다. rename은 source와 destination을 모두 선언하고 신규·삭제 file도 file operation target으로 선언한다. 이 입력은 COW/worktree/clone이 없는 shared-checkout implementation admission에만 사용한다.
  - `PreviewRequest`: 같은 판정기를 side effect 없이 실행할 project/workspace와 optional work identity다.
  - `ProjectWorkflowAdapter`: project-owned artifact contract를 normalized active pair, submission completeness, review verdict, USER_REVIEW blocker와 completion state로 반환한다.
  - `ClientProcessSpec`: Flutter/Unity executable reference, launch/restart policy, local socket endpoint와 Unity-to-Flutter detail command capability다.
- 내부 durable type:
  - `WorkspaceSnapshot`: canonical root, Git revision, tracked·untracked·dirty content, file mode/symlink와 config/grant revision의 exact base fingerprint다.
  - `OverlayWorkspace`: task identity, base snapshot, writable layer, task가 읽는 merged view, task-local temp/cache, isolated Git metadata reference와 retention state다.
  - `ChangeSet`: review PASS 뒤 동결된 additions/modifications/deletions, mode/symlink operation, base fingerprint, actual write-set, validation evidence와 content-addressed identity다.
  - `IntegrationRecord`: task dispatch ordinal, change-set revision과 integration attempt ordinal, expected/observed before fingerprint, managed predecessor set, apply/validation/rollback 결과, after fingerprint, integrated/terminal-deferred state, blocker와 cleanup state다.
  - `PlanWriteClaim`: canonical workspace identity, normalized file key, task/PLAN identity와 content revision, owner dispatcher PID/start token, active role·attempt locator, acquired/renewed 시각과 lifecycle state다. 한 task의 전체 key는 all-or-none으로 획득·이관한다.
  - `PlanWriteClaimLedger`: 같은 physical shared checkout의 모든 task group이 공유하는 versioned file-key map과 atomic mutation lock이다. dispatcher restart와 `--task-group` 범위 변경에도 기존 claim을 보존하며 task group별 빈 ledger를 만들지 않는다.
- 출력:
  - `RouteDecision`: 외부에 노출할 provider/model 하나와 내부에 저장할 ordered candidate, rule/reason, eligibility/rejection와 used history다.
  - `RuntimeEvent`: execution/attempt, project/work-unit/stage, overlay/change-set/integration lifecycle, stream/heartbeat, config/quota reference와 terminal result다.
  - `ProviderStatus`: official provider/model/profile id, readiness, capability, quota/status와 오류 근거다.
  - `PreviewResult`: 실행과 같은 selection/dependency/blocker 판단 및 no-side-effect 증명이다.
  - `ProjectLogRecord`: route, quota, process/session·overlay locator, task별 loop/attempt, failure/retry/failover/review/change-set/integration/completion을 연결한다.
  - `ClientProcessStatus`: client kind, PID/start identity, connected/crashed/stopped lifecycle와 last command/result다.
  - `AdmissionStatus`: workspace grant, provider unattended/bypass와 scope 검증 결과, blocker code와 누락 설정이다.
  - `PlanAdmissionStatus`: shared-checkout task의 claimed/waiting/blocked 상태, 충돌 owner와 normalized file key, invalid manifest reason, release/transfer/reconciliation 결과다.
  - `UserNotification`: agent 미호출 또는 change-set 미통합 상태, project/provider/profile, 실패한 preflight·merge·validation과 bypass/workspace/해결 안내다.
  - `IntegrationStatus`: task/change-set, ordinal, queued/integrating/integrated/blocked, conflict path, retained overlay와 recovery action이다.
- 금지:
  - Node와 `iop-agent` host에 provider 또는 AgentTaskManager 구현을 복사하지 않는다.
  - Python process, function name, marker와 persisted key를 production 계약으로 가져오지 않는다.
  - parity matrix와 Go 대체 evidence가 고정되기 전에 Python 참조 구현을 폐기하거나, Milestone 완료 뒤 production/fallback 경로로 남기지 않는다.
  - malformed checkpoint/route/locator를 빈 상태나 현재 정책으로 조용히 초기화·재선택하지 않는다.
  - Flutter·Unity가 provider 선택, task scheduling, retry/failover 또는 project state를 다시 소유하지 않는다.
  - worker exit code나 완료 문구만으로 review-ready/completed를 확정하지 않는다.
  - runtime이 repo-global 설정이나 project 작업 파일에 장비 경로·checkpoint·client process 상태를 기록하지 않는다.
  - Flutter·Unity가 daemon이나 서로를 직접 시작·종료하지 않는다. client process control은 `iop-agent`를 경유한다.
  - 같은 OS 사용자 밖의 client를 app token 없이 신뢰하지 않는다.
  - runtime `WORK_LOG`/heartbeat 변화만 review progress로 세지 않는다.
  - 등록되지 않았거나 canonical containment를 벗어난 workspace에서 agent를 호출하지 않는다. worktree의 외부 git common dir는 명시 metadata allowance 없이 쓰지 않는다.
  - unattended/approval-bypass와 workspace scope guardrail 중 하나라도 검증되지 않은 provider/profile을 대화형 승인 fallback으로 호출하지 않는다.
  - workspace grant를 외부 서비스 mutation, 다른 project 또는 임의 장비 경로의 포괄 승인으로 확장하지 않는다.
  - 병렬 task process가 canonical workspace file, 공용 Git index/ref 또는 다른 task writable layer를 직접 변경하지 않는다.
  - task별 build/temp/cache 출력을 공용 mutable path에 기록해 다른 실행과 섞지 않는다.
  - review PASS와 change-set validation 전 결과를 canonical base에 적용하거나, 완료 속도에 따라 integration 순서를 바꾸지 않는다.
  - 관리되지 않은 base drift에 blind apply하거나 merge conflict를 자동 overwrite하지 않는다. 실패한 apply는 exact pre-integration state로 rollback한다.
  - durable IntegrationRecord와 blocker evidence 전에 overlay를 삭제하지 않는다. 실제 Git branch/index/commit이 필요한 task는 명시된 worktree/clone fallback 없이 default overlay에서 수행하지 않는다.
  - 한 change set의 terminal-deferred blocker로 뒤의 independent integration queue를 멈추지 않는다. 해결된 결과를 과거 ordinal에 끼워 넣지 않고 새 immutable change-set revision과 attempt로 다시 검증한다.
  - shared checkout에서 valid write claim 전체를 얻기 전에 worker/selfcheck/official review를 시작하거나, `Modified Files Summary`의 교집합을 명시 predecessor나 roadmap dependency로 변환하지 않는다.
  - PLAN target을 LLM으로 추출·보정하거나 누락·중복·빈 값·glob·workspace 밖·directory target을 empty/disjoint write-set으로 간주하지 않는다.
  - model process 종료, WARN/FAIL review 또는 dispatcher restart만으로 claim을 해제하지 않는다. follow-up PLAN은 기존 claim을 먼저 놓지 않고 새 집합으로 원자 이관하며, verified completion 또는 task mutation의 안전한 정리와 live owner 부재 전에는 release하지 않는다.
  - shared-checkout compatibility claim을 독립 COW writable layer, 격리 worktree 또는 full clone 사이의 논리적 dependency나 병렬 실행 금지로 확장하지 않는다.
  - file write-set이 disjoint하다는 이유만으로 shared checkout의 build/test 결과를 task-isolated evidence로 간주하지 않는다. 다른 active mutation이 없는 stable source 또는 task별 격리 workspace에서 final verification을 다시 수행하지 않은 결과로 Roadmap Completion을 확정하지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `common-runtime` | Node와 `iop-agent`가 같은 provider profile을 선언했다 | run, stream, resume와 cancel을 각각 수행한다 | 두 host가 같은 common implementation과 lifecycle/failure 의미를 사용하고 중복 구현이 없다 |
| S02 | `provider-catalog` | provider가 설치·인증됨, 미설치, 미인증 또는 model 미지원 상태다 | discovery와 status를 실행한다 | 공식 provider/model/profile 이름으로 readiness가 반환되고 실행 불가 상태는 구체적인 오류가 된다 |
| S03 | `task-manager` | 등록 project에 미선택 ready Milestone과 수동 시작 뒤 중단된 Milestone이 있다 | daemon 시작과 manual start/resume을 수행한다 | 미선택 Milestone은 자동 시작하지 않고, 수동 시작된 작업은 끝까지 진행하며 중단 작업은 기본 자동 재개와 local override를 따른다 |
| S04 | `node-consumer` | 기존 Node run/session/status 요청과 config fixture가 있다 | Node를 common runtime bridge로 전환한다 | 기존 Edge-Node wire 의미와 provider behavior가 보존되고 Node 내부 duplicate provider가 없다 |
| S05 | `config-registry` | repo-global defaults와 user-local project/device override, 겹치는 ordered rules 및 실행 중 revision 변경이 있다 | config를 load/watch한다 | local override와 array 전체 교체가 결정적으로 적용되고 repo 파일은 쓰지 않으며 현재 실행은 기존 revision, 다음 호출은 새 revision을 사용한다 |
| S06 | `target-policy` | 시간·quota·stage·grade 조건이 겹치고 persisted route가 있거나 손상됐다 | selection 또는 resume한다 | 첫 일치 rule의 provider/model 하나가 반환되고 판단 이력이 저장되며 손상 상태는 silent reselection 없이 오류가 된다 |
| S07 | `quota-failure` | provider별 available/exhausted/unknown/not-applicable와 runtime quota error가 있다 | admission, 실행 실패와 failover를 처리한다 | typed evidence와 immutable snapshot이 격리되고 알려진 정책 안에서만 retry/failover하며 unknown은 work-unit blocker가 된다 |
| S08 | `workflow-evidence` | worker/selfcheck/review artifact에 완성, placeholder, identity mismatch와 Pi selfcheck 후 미완성이 있다 | submission/review gate를 평가한다 | 모든 provider에 같은 matcher가 적용되고 Pi만 같은 native context repair 후 재검증하며 통과 전 official review는 호출되지 않는다 |
| S09 | `state-recovery` | duplicate daemon, duplicate workspace manager, restart, live child, corrupt checkpoint, partial archive와 failure budget이 있다 | device singleton/workspace lease 획득과 reconciliation을 수행한다 | 장비와 workspace의 invocation owner는 각각 하나이고 valid live work를 중복 실행하지 않으며 불명확 상태는 추정 복구 없이 blocker/error가 된다 |
| S10 | `cli-surface` | binary와 repo-global/local 설정만 설치된 로그인 macOS 환경이다 | validate, list, preview, serve, select/start, stop/resume와 status command를 사용한다 | UI 없이 설정·수동 시작·자동 재개·제어·관측이 가능하고 미선택 ready Milestone은 실행되지 않는다 |
| S11 | `local-control` | 같은 OS 사용자의 둘 이상의 후속 client가 같은 `iop-agent` 상태를 소비한다 | local control contract와 socket permission/peer 경계를 검증한다 | 같은 OS 사용자 client는 별도 app token 없이 신뢰되고 다른 사용자 접근은 거부되며 UI/runtime 책임이 분리된다 |
| S12 | `project-logs` | 같은 task의 pair loop 11 retry/follow-up과 다른 task의 병렬 loop, 독립 work-log archive ordinal이 있다 | START/FINISH와 completion archive를 기록·복구한다 | task별 loop/attempt/locator가 안정되고 terminal closure 뒤에만 exactly-once archive와 cleanup이 수행된다 |
| S13 | `parity-cutover` | 기존 combined SDD, 안정화된 Python 작업·결과, Node behavior와 완료된 selector evidence가 있다 | 각 동작을 absorb/replace/not-applicable로 분류하고 Go cutover를 검증한다 | canonical workspace 직접 병렬 쓰기는 `replace`로 기록되고 미분류 동작, Python runtime 의존성, 정적 route/cap 문구와 Node duplicate implementation이 남지 않으며 Milestone 완료 전환 시 Python 구현이 폐기된다 |
| S14 | `logged-smoke` | 실제 로그인된 provider와 둘 이상의 등록 project/clone workspace가 있다 | discovery부터 실행, quota, cancel, 재호출, restart와 completion까지 수행한다 | credential을 기록하지 않고 project별 로그와 E2E evidence가 남으며 한 project 오류가 다른 project를 멈추지 않는다 |
| S15 | `client-process-manager` | Flutter·Unity fixture executable과 단절·crash·중복 launch가 있다 | daemon이 client lifecycle과 Unity detail command를 처리한다 | client는 하나씩만 실행·재연결되고 Unity 요청은 daemon을 통해 Flutter start/focus로 중계되며 client 종료가 daemon을 끝내지 않는다 |
| S16 | `task-manager` | explicit predecessor가 충족된 independent sibling의 write-set이 비중첩, 중첩 또는 unknown이고 별도 workspace instance도 있다 | concurrency admission을 평가한다 | 번호와 write-set 겹침에서 암묵 dependency를 만들지 않고 provider 한도 안에서 sibling을 task별 격리 mode로 dispatch하며 명시 predecessor만 실행을 막는다 |
| S17 | `guardrail-admission` | 등록/미등록 workspace, full clone/worktree, symlink escape, writable-root confinement 가능/불가와 unattended/approval-bypass on/off provider profile이 있다 | start/resume preflight를 수행한다 | 등록 canonical grant와 명시 VCS metadata allowance 안에서 bypass와 task isolation을 함께 강제할 수 있는 profile만 agent를 호출하고, 나머지는 invocation 0회인 typed blocker와 bypass/workspace 설정 안내를 내며 독립 project는 계속 진행한다 |
| S18 | `overlay-workspace` | 같은 dirty canonical workspace의 dependency-ready task 둘이 같은 파일과 서로 다른 파일을 수정하고 build output을 생성하며 canonical absolute path 쓰기도 시도한다 | 두 unattended/bypass provider를 동시에 실행하고 worker·selfcheck·review가 task view를 이어서 사용한다 | 두 task는 동일 pinned base와 각자 변경만 보고 canonical file·공용 Git index/ref·상대 task layer를 변경하지 못하며 temp/cache도 섞이지 않는다 |
| S19 | `change-set-integration` | 동시 task의 clean/disjoint·same-file conflict change set, managed predecessor merge, unmanaged base drift, post-apply 검증 실패와 daemon restart가 있다 | dispatch ordinal 순서로 serial integration과 recovery를 수행한다 | clean three-way 결과만 자동 반영되고 conflict·unmanaged drift·검증 실패는 partial mutation 없이 overlay를 보존한 terminal-deferred task blocker가 되며 뒤의 independent change set은 계속되고 해결된 revision과 restart도 중복·순서 역전 없이 재개된다 |
| S20 | `shared-checkout-write-lock` | 같은 physical shared checkout의 여러 task group에 disjoint·overlapping target, invalid manifest, 통제되지 않은 PLAN revision 변경과 review follow-up이 있고 정책 도입 전에 일부 작업이 이미 실행 중이었다 | dispatcher가 workspace 공통 admission, restart reconciliation, follow-up transfer와 completion/reset release를 수행한다 | disjoint valid PLAN만 병렬 시작하고 overlap은 dependency 생성 없이 대기하며 invalid/ambiguous state는 invocation 0회로 막힌다. claim은 전체 review lifecycle과 follow-up에 유지·원자 이관되고 verified completion 또는 mutation 정리가 검증된 reset 뒤 해제되며 격리 mode는 제한하지 않는다. shared-checkout file claim은 build/test 격리 evidence로 과장하지 않고, 정책 도입 전 실행된 작업도 다른 active mutation이 없는 stable source 또는 격리 workspace에서 final verification을 다시 통과한다 |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | 완료 Evidence 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | common provider conformance와 duplicate implementation search | `agent-task/m-iop-agent-cli-runtime/...` | `common-runtime` Roadmap Completion과 Node/CLI test output |
| S02 | provider discovery/status table test와 authenticated smoke | `agent-task/m-iop-agent-cli-runtime/...` | `provider-catalog` Roadmap Completion과 readiness/error evidence |
| S03 | manual start/default auto-resume 및 multi-project scheduler integration test | `agent-task/m-iop-agent-cli-runtime/...` | `task-manager` Roadmap Completion과 no-supervisor/no-unselected-start trace |
| S04 | Node wire/config compatibility suite | `agent-task/m-iop-agent-cli-runtime/...` | `node-consumer` Roadmap Completion과 기존 contract conformance evidence |
| S05 | repo-global/local merge, read-only repo, invalid config, watcher와 revision integration test | `agent-task/m-iop-agent-cli-runtime/...` | `config-registry` Roadmap Completion과 revision A/B 및 clean repo trace |
| S06 | ordered selector, persisted route와 tamper matrix | `agent-task/m-iop-agent-cli-runtime/...` | `target-policy` Roadmap Completion과 selected rule/reason/history evidence |
| S07 | quota parser, runtime observation, isolation과 failover test | `agent-task/m-iop-agent-cli-runtime/...` | `quota-failure` Roadmap Completion과 snapshot/failure transition evidence |
| S08 | provider-neutral matcher와 Pi same-context repair matrix | `agent-task/m-iop-agent-cli-runtime/...` | `workflow-evidence` Roadmap Completion과 review invocation/locator evidence |
| S09 | device singleton/workspace lease, process identity, checkpoint, restart와 archive fault matrix | `agent-task/m-iop-agent-cli-runtime/...` | `state-recovery` Roadmap Completion과 no-duplicate/exact-state evidence |
| S10 | binary와 split config CLI command integration test | `agent-task/m-iop-agent-cli-runtime/...` | `cli-surface` Roadmap Completion과 headless operation transcript |
| S11 | 신규 local control agent-contract와 OS-user socket boundary test | `agent-task/m-iop-agent-cli-runtime/...` | `local-control` Roadmap Completion, contract link와 same-user/other-user trace |
| S12 | WORK_LOG loop/attempt/locator, dynamic frontier와 archive reconciliation fixture | `agent-task/m-iop-agent-cli-runtime/...` | `project-logs` Roadmap Completion과 exactly-once archive evidence |
| S13 | disposition-complete parity matrix, stale dependency·duplicate·Python fallback search와 Python 구현 폐기 evidence | `agent-task/m-iop-agent-cli-runtime/...` | `parity-cutover` Roadmap Completion과 zero-unclassified/zero-match 및 Python 폐기 evidence |
| S14 | actual logged-in macOS multi-project field smoke manifest | `agent-task/m-iop-agent-cli-runtime/...` | `logged-smoke` Roadmap Completion과 redacted environment/result manifest |
| S15 | fixture Flutter/Unity process ownership, crash/reconnect와 detail command test | `agent-task/m-iop-agent-cli-runtime/...` | `client-process-manager` Roadmap Completion과 PID/start/focus lifecycle trace |
| S16 | explicit dependency, write-set과 isolation-mode concurrency matrix | `agent-task/m-iop-agent-cli-runtime/...` | `task-manager` Roadmap Completion과 dependency-only admission/parallel dispatch trace |
| S17 | canonical/symlink/VCS metadata containment, writable-root enforcement와 provider unattended/bypass preflight matrix | `agent-task/m-iop-agent-cli-runtime/...` | `guardrail-admission` Roadmap Completion과 allowed/blocked/zero-invocation/notification trace |
| S18 | dirty/untracked/mode/symlink base snapshot, overlapping task overlay, canonical absolute-path denial과 Git/temp/cache isolation test | `agent-task/m-iop-agent-cli-runtime/...` | `overlay-workspace` Roadmap Completion과 identical-base/no-cross-write/canonical-unchanged trace |
| S19 | ordinal, clean/conflict, managed/unmanaged drift, validation rollback, terminal-deferred queue advance, retry revision, restart와 retention fault matrix | `agent-task/m-iop-agent-cli-runtime/...` | `change-set-integration` Roadmap Completion과 atomic auto-merge/blocker/no-duplicate IntegrationRecord |
| S20 | PLAN table parser, alias/rename/containment, cross-task-group atomic overlap admission, PLAN revision mutation, follow-up transfer, crash/restart ownership과 completion/reset safe release deterministic test 및 정책 도입 전 active work stable rerun | `agent-task/m-iop-agent-cli-runtime/...` | `shared-checkout-write-lock` Roadmap Completion, same-repository dispatcher PLAN의 test output과 shared-checkout 작업의 stable/isolated final verification evidence |

## Cross-repo Dependencies

- 없음. 같은 IOP monorepo 안에서 공통 package, Node bridge, `iop-agent` binary와 protocol source를 관리한다.
- shared-checkout dispatcher 변경은 같은 repository의 별도 `dev` clone에서 독립 PLAN으로 구현·검증한 뒤 현재 Milestone의 `shared-checkout-write-lock` evidence로 통합한다. 이는 별도 repository 의존성이나 `.agent-roadmap-sync/locks.yaml` 대상이 아니다.
- 구현 선행 기준은 완료된 [Agent Task 동적 실행 Target Selector](../../../phase/automation-runtime-bridge/milestones/agent-task-runtime-target-selector.md)의 결과다.
- [Pi CLI Provider Integration](../../../../phase/automation-runtime-bridge/milestones/pi-cli-provider-integration.md)과 [CLI Agent Group Grade Routing](../../../../phase/automation-runtime-bridge/milestones/cli-agent-group-grade-routing.md)은 구현 잠금 선행 조건이 아니라 현재 Python 안정화 결과와 요구사항을 parity 입력으로 사용하는 참조·연결 작업이다.

## Drift Check

- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- [x] Evidence Map이 code-review/complete.log에서 검증 가능하다.
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- [x] D01~D05는 [user_review_0.log](user_review_0.log), D06은 [user_review_1.log](user_review_1.log)에 반영됐고 사용자 결정 항목이 남지 않았다.

## 사용자 리뷰 이력

- 2026-07-28: 스킬 기반 1차 테스트로 안정화된 Python 작업과 이전 Milestone·결과물을 parity 입력으로 사용하고, Go parity와 cutover evidence 확보 뒤 Milestone 완료 전환 시 Python 구현을 폐기하기로 결정했다.
- 2026-07-28: 새 Milestone 선택·최초 시작은 항상 수동이고, 시작 기록이 있는 중단 작업의 자동 재개만 기본 on이며 local 설정으로 조정하기로 했다.
- 2026-07-28: repo-global read-only 공통 설정과 user-local 장비·project 설정/상태를 분리하고 local override를 뒤에 적용하기로 했다.
- 2026-07-28: 스킬의 explicit predecessor grammar는 유지하되 canonical workspace에 직접 병렬 쓰는 동작은 Go runtime의 workspace isolation으로 대체하기로 했다.
- 2026-07-28: 개인 장비의 소유 OS 사용자 범위에서 단일 `iop-agent`가 다중 project와 Flutter·Unity subprocess를 소유하고, 같은 OS 사용자 local proto client를 신뢰하기로 했다.
- 2026-07-28: 완전 자동화를 기본으로 하고, 등록 canonical workspace 범위에서는 모든 provider action을 사전 승인한다. `iop-agent`가 unattended/approval-bypass와 workspace guardrail을 선검증하며 미충족이면 agent를 호출하지 않고 bypass 설정 안내 알림을 내기로 했다.
- 2026-07-28: 같은 workspace의 dependency-ready task는 pinned base 위의 task별 COW writable layer에서 병렬 실행하고 review PASS change set을 dispatch ordinal 순서로 자동 직렬 통합하며, conflict·검증 실패·관리되지 않은 base drift는 overlay를 보존한 blocker로 처리하기로 했다.
- 2026-07-29: COW/worktree/clone 격리가 적용되지 않은 project shared checkout에서는 PLAN target file을 workspace 공통 dispatcher ledger가 runtime claim하고, 겹치는 task는 dependency 추가 없이 대기시키며 verified completion 또는 mutation 정리가 검증된 reset 뒤 claim을 해제하기로 했다. 이 file claim은 disjoint target의 read/build/test 격리를 보장하지 않으므로 final verification은 stable source 또는 격리 workspace에서 수행하고, dispatcher 구현은 같은 repository의 별도 `dev` clone에서 독립 PLAN으로 진행한다.

## 작업 컨텍스트

- 표준선: 개인 장비의 소유 OS 사용자 범위에서 단일 active `iop-agent`가 headless runtime·CLI, 다중 project와 Flutter·Unity subprocess를 소유한다. Node는 공통 library consumer이지 두 번째 supervisor가 아니다.
- 표준선: repo-global 설정은 비밀정보 없는 공통 기본값·정책 템플릿을 버전 관리하고 runtime이 읽기만 한다. user-local store는 장비 경로, provider 실행 참조, project registry/override, 자동 재개, client launch와 checkpoint/lease를 소유하고 global 뒤에 적용한다.
- 표준선: 새 Milestone 선택·최초 시작은 항상 수동이며 시작 기록이 있는 중단 작업 자동 재개만 기본 on이다. `auto_resume_interrupted` local 설정으로 자동 재개 여부만 조정한다.
- 표준선: provider authentication과 credential은 각 CLI가 소유한다. 등록 canonical workspace는 해당 범위의 agent action을 사전 승인하며 unattended/approval-bypass가 기본이다. `iop-agent`는 workspace containment와 provider bypass capability를 dispatch 전에 검증하고, 미충족이면 대화형 fallback 없이 해당 work unit을 막고 설정 안내 알림을 낸다.
- 표준선: Node와 `iop-agent`는 공통 provider/manager package를 소비하고 host-specific wire, command와 lifecycle adapter만 가진다.
- 표준선: Python 작업, 이전 결과물과 [기존 SDD](../../../sdd/automation-runtime-bridge/shared-agent-task-runtime-desktop-agent/SDD.md)는 provider, scheduler, workflow artifact, review/finalization, process/session, quota/error, log/reconciliation 전 영역의 behavior fixture다. 구현 중 각 동작을 `absorb | replace | not-applicable`로 분류하고, Go parity와 cutover evidence 확보 뒤 Milestone 완료 전환 시 Python 구현을 폐기해 production dependency나 fallback으로 남기지 않는다.
- 표준선: explicit predecessor만 dependency로 사용한다. 서로 다른 workspace instance와 같은 canonical workspace의 independent sibling을 병렬 dispatch하며, same-workspace task는 동일 pinned base를 읽는 독립 COW writable layer에서 실행한다. review PASS change set은 dispatch ordinal 순서로 하나씩 자동 통합하고 conflict·검증 실패·관리되지 않은 base drift는 원본 overlay를 보존한 task-local blocker가 된다.
- 표준선: COW/worktree/clone이 적용되지 않은 project implementation shared checkout에서는 PLAN `Modified Files Summary`를 workspace 전체 task group의 deterministic file claim 입력으로 사용한다. claim 충돌은 기능 의존성이 아닌 같은 target file의 동시 수정 admission wait이고, claim은 worker부터 official review와 follow-up까지 유지·원자 이관한다.
- 표준선: file claim은 disjoint target task가 서로의 미완성 파일을 읽는 build/test까지 격리하지 않는다. 모든 shared-checkout 완료 evidence는 다른 active mutation이 없는 stable source 또는 격리 workspace에서 final verification을 다시 수행하며, 정책 도입 전 실행된 작업은 번호나 현재 active/archive 위치와 무관하게 claim 보호가 소급됐다고 간주하지 않는다.
- 표준선: local proto-socket은 binary가 소유하고 같은 OS 사용자 client를 신뢰하는 경계다. Flutter와 Unity는 서로 직접 통신하거나 실행하지 않고, Unity의 상세 UI 요청은 `iop-agent`가 Flutter start/focus로 중계한다.
- 표준선: workspace grant의 mutation 범위는 canonical project root와 명시된 VCS metadata root뿐이며 외부 서비스 mutation이나 다른 project 권한을 포함하지 않는다. task process가 아니라 `iop-agent` integration owner만 canonical base를 변경한다. worktree는 공유 git common dir가 root 밖에 있으므로 실제 Git fallback을 선택할 때 정확한 metadata allowance를 별도로 고정한다.
- 표준선: command 이름, package/file 배치, proto field, retry backoff 수치와 log serialization은 기존 구조와 표준안으로 정하고 사용자 결정으로 올리지 않는다.
- 후속 SDD: Flutter Desktop 설정·운영 UI와 Unity 3D Character Milestone을 만들 때 각각 필요 여부를 판정한다.
