- dispatch.py: unknown isolation, selfcheck policy, admission quota, target cap 제거 로직 구현 - select_execution_target.py: 대상 선택器 개선 - test_dispatch.py: 전체 시나리오 테스트 대폭 추가/리팩토링 - test_select_execution_target.py: 신규 테스트 추가 - ROADMAP/PHASE/SDD 마일스톤 업데이트: unknown_isolation 완료, cli-agent-group-grade-routing 종료 - archive: 07~10번 subtask를 archive로 이동 - WORK_LOG 갱신, agent-test/inventory-dev.yaml 동기화 - shared-agent-task-runtime-desktop-agent SDD 신규 생성
24 KiB
24 KiB
SDD: 공통 Agent Task Runtime과 Desktop Agent
위치
- Milestone: 공통 Agent Task Runtime과 Desktop Agent
- Phase: PHASE.md
상태
[검토중]
SDD 잠금
- 상태: 잠금
- 사용자 리뷰: USER_REVIEW.md
- 잠금 항목:
- [D01] macOS에서 창 닫기, 명시 Quit, 로그인 시 시작이 Desktop runtime과 실행 중 agent에 미치는 background lifecycle을 결정한다.
문제 / 비목표
- 문제: 이미 agent-ops 작업 체계를 가진 project의 Agent Task 실행·관측·복구 책임은 Python dispatcher를 모델이 감시하는 스킬 흐름과 Node 내부 CLI runtime에 나뉘어 있다. 이 구조는 감시 모델의 토큰 비용을 지속 발생시키고, Desktop Agent를 만들 때 provider 실행을 복사할 위험이 있다. 이 SDD는 project-owned workflow 의미는 유지한 채 provider와 AgentTaskManager를 단일 Go runtime으로 모으고 Node와 Edge 없는 Desktop host가 함께 소비하는 책임, lifecycle, config와 evidence 경계를 고정한다.
- 비목표:
- 기능을 줄인 MVP나 일부 provider만 동작하는 선행판을 정의하지 않는다.
- Python 코드를 production에서 호출·이식하거나 Python process의 활성 상태를 Desktop으로 이전하지 않는다.
- Desktop에 Edge, Control Plane, remote terminal bridge를 포함하지 않는다.
- CLI 인증·credential·계정 회전, 외부 알림, 완성형 설정 UI, Windows/Linux, signing/notarization은 다루지 않는다.
- agent-ops 공통 skill/rule을 runtime monitoring engine으로 바꾸지 않는다.
- agent-ops를 사용하지 않는 일반 사용자 요청을 direct/Plan/Milestone으로 분류하거나 IOP 소유 skill/tool call로 작업 파일을 생성하는 상위 오케스트레이션을 구현하지 않는다.
Source of Truth
| 영역 | 기준 | 메모 |
|---|---|---|
| Roadmap | 공통 Agent Task Runtime과 Desktop Agent | 제품 목표, 전체 동등성 범위, 기능 Task와 제외 범위 |
| Policy | Agent Task 동적 실행 Target Selector, CLI Agent Group Grade Routing | 선택, route pin, quota, failover와 agent/model rule의 우선 기준 |
| Project Workflow | 등록 workspace의 agent-ops/rules/project, agent-ops/skills/project, Milestone·Plan·Code Review 파일 |
workflow skill/rule과 작업 파일 의미는 project가 소유하고 runtime은 이미 선택된 work step을 실행한다 |
| Separate Orchestration | 에이전트 작업 루프 오케스트레이션 MVP | agent-ops를 직접 쓰지 않는 사용자의 일반 요청 분류, IOP 소유 Plan/Milestone skill과 합성 tool call은 별도 상위 기능이다 |
| Reference Behavior | agent-ops/skills/project/orchestrate-agent-task-loop/scripts, active agent-task/**/WORK_LOG.md |
Python은 동작·오류·관측 evidence일 뿐 production dependency가 아니다 |
| Code | packages/go/agentruntime, apps/node, apps/desktop-agent, apps/desktop-agent-ui, packages/go/config |
단일 runtime 구현과 host integration의 구현 source of truth 후보 |
| Existing Contract | Edge-Node Runtime Wire, Edge Config Runtime Refresh | Node bridge가 보존해야 할 현재 wire/config 의미. 변경이 필요하면 구현 전 agent-contract를 갱신한다 |
| Project State | 각 등록 workspace의 agent-task, agent-roadmap, WORK_LOG.md, agent-log |
작업 원문·진행·evidence의 durable source of truth |
| App State | app-owned YAML config tree, project registry, 최소 runtime checkpoint | provider/global 설정과 registry id별 project override를 모두 소유한다. workspace config나 project 작업 원문을 중앙 권위로 복제하지 않는다 |
| External Provider | 사용자가 YAML에 선언하고 이미 인증한 CLI provider | app은 discovery/readiness/status/실행만 하며 인증을 소유하지 않는다 |
| User Decision | D01 | macOS window/quit/login-start lifecycle만 사용자 결정으로 남기고 package/API/config/state 세부는 문서의 표준선을 따른다 |
State Machine
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|---|---|---|---|
host-starting |
Node process 또는 Desktop app이 시작됐다 | config-validating 또는 host-error |
process/app lifecycle |
config-validating |
app-owned YAML의 global 설정과 registry id별 project override를 읽고 schema·provider reference를 검증한다 | provider-discovering 또는 config-error |
config revision과 validation event |
provider-discovering |
선언 provider의 binary/version/authenticated readiness/model capability를 read-only 조회한다 | project-watching 또는 provider-error |
discovery snapshot |
project-watching |
명시 등록 workspace의 기존 agent-ops task/roadmap watcher와 app-owned config watcher가 활성화됐다 | idle, work-ready, config-pending 또는 stopped |
project registry와 watcher event |
config-pending |
agent 실행 중 새 유효 config revision이 관측됐다 | running 후 work-ready 또는 idle |
현재 execution은 기존 snapshot을 유지하고 다음 호출에 새 revision 적용 |
work-ready |
기존 agent-task 또는 기존 priority-queue의 최상위 ready Milestone이 있다 | selecting 또는 stopped |
project filesystem scan. 일반 요청으로 새 work item을 합성하지 않는다 |
selecting |
stage/work-unit/config revision/quota snapshot으로 ordered rules를 평가한다 | running 또는 selection-error |
하나의 provider/model route decision |
running |
provider process가 immutable route/config snapshot으로 시작됐다 | running, retrying, failing-over, completed, failed 또는 cancelling |
runtime stream, heartbeat, process/session locator |
retrying |
알려진 동일-target 복구 가능 오류와 failure budget이 남았다 | running 또는 failed |
typed failure와 transition history |
failing-over |
선언 정책이 허용하는 quota/context/model/stream 오류가 확인됐다 | running 또는 failed |
logical context package와 route transition |
cancelling |
사용자가 project/app 자동 실행을 중단했다 | stopped 또는 failed |
cancel event, process group/session termination evidence |
completed |
현재 stage가 성공하고 work log/filesystem이 반영됐다 | work-ready 또는 idle |
terminal event와 project evidence |
failed |
unknown 오류, invalid provider/config, eligible target 부재 또는 failure budget 소진이 발생했다 | idle, 독립 project의 work-ready, 또는 사용자 수정 뒤 config-validating |
surfaced error와 project log |
stopped |
auto-run off, project stop 또는 D01에서 정의한 app 종료가 완료됐다 | config-validating 또는 project-watching |
명시 재시작/enable event |
Interface Contract
- 계약 원문: 현재 Node 경계는 Edge-Node Runtime Wire와 Edge Config Runtime Refresh를 유지한다. 공통 runtime host/config/event schema는 구현 계획 전에 agent-contract create/update gate로 별도 고정한다.
- 입력:
HostConfig: host kind, app-owned config revision, provider catalog, selection policy, log/state root, background lifecycle와 runtime defaults다.ProjectRegistration: stable project registration id, canonical workspace instance, enabled/auto-run flag와 app config tree 안의 override key다. workspace-local runtime config location을 가리키지 않는다.WorkRequest: project/workspace identity, project workflow가 이미 선택한 task 또는 Milestone path, stage/work-unit identity, dependency state와 optional persisted route다. 일반 사용자 요청이나 direct/Plan/Milestone 분류 입력이 아니다.ProviderProfile: stable provider/profile id, executable/args/env reference, renderer/emitter/session/status capability와 approval bypass mapping이다.SelectionPolicy: default provider/model과 ordered conditional rule array다. app registry의 project override를 적용한 뒤 평가하며 첫 일치 rule이 승리한다.QuotaSnapshot: provider credential/profile별 app-globalavailable | exhausted | unknown | not_applicable, cap/remaining/reason/snapshot id/checked-at이다. 같은 key를 쓰는 모든 project가 공유한다.ConfigRevision: 현재 execution이 고정한 immutable revision이다. watcher의 새 revision은 다음 agent invocation 전에만 교체한다.
- 출력:
RouteDecision: 정확히 하나의 provider/profile/model, reason, matched rule index, quota snapshot, config revision과 evaluated-at이다.RuntimeEvent: start, stdout/stderr delta, normalized delta, heartbeat, session/process locator, retry/failover, complete, error, cancelled다.FailureEvidence: typed class, source, provider-confirmed 여부, bounded evidence, signal과 retry/failover eligibility다.ProjectLogRecord: execution/work-unit/project identity로 route, quota, config, process/session, stream/heartbeat, transition과 terminal 결과를 연결한다.ProviderStatus: 외부에는 official provider/model/profile id로 보이는 discovery/readiness/authenticated state, capability, app-global quota/status와 오류 근거다. genericcliadapter는 Desktop 주 식별자가 아니다.
- 금지:
- WorkRequest를 일반 사용자 요청 triage 계약으로 확장하거나 그 입력만으로 runtime core가 direct/Plan/Milestone을 분류·생성하지 않는다.
- project-owned agent-ops Milestone·Plan·Code Review skill/rule을 공통 runtime에 복사·내장하거나 IOP 소유 의미로 재정의하지 않는다.
- Node와 Desktop host에 provider renderer/emitter/quota/error parser를 복제하지 않는다.
- Desktop Agent가 Edge를 기동하거나 Edge config를 app 내부 실행 조건으로 요구하지 않는다.
- Python process, Python module 또는 monitoring skill 호출을 runtime correctness에 사용하지 않는다.
- invalid config/provider/auth/model/capability 또는 unknown 오류를 silent fallback으로 숨기지 않는다.
- generic stderr만으로 provider quota/context/model failure를 확정하지 않는다.
- 실행 중 config revision이나 시간 경계만으로 현재 work unit의 pinned route를 바꾸지 않는다.
- credential/token/raw secret을 app discovery result나 project log에 기록하지 않는다.
- app store에 project의 roadmap/task/work-log 원문을 중앙 복제해 별도 source of truth를 만들지 않는다.
Acceptance Scenarios
| ID | Milestone Task | Given | When | Then |
|---|---|---|---|---|
| S01 | provider-runtime |
Node와 Desktop이 동일 provider profile을 선언했다 | 각각 짧은 실행과 cancel을 수행한다 | 동일 common provider implementation과 event/failure 의미를 사용하고 host별 provider 복제가 없다 |
| S02 | agent-task-manager |
agent-ops 작업 체계를 가진 project에 기존 task와 ready Milestone이 있다 | supervisor 모델 없이 auto-run을 시작한다 | 남은 task를 먼저 처리하고 이후 기존 priority queue를 순차 소비한다 |
| S03 | host-boundary |
Node run request와 Desktop local request가 같은 logical provider target을 가리킨다 | 두 host가 실행한다 | Node는 기존 wire event로, Desktop은 local event로 변환하되 core lifecycle은 동일하다 |
| S04 | parity-cutover |
current Milestone/work log, Python reference와 Node behavior inventory가 있다 | 동등성 matrix와 cutover 검증을 수행한다 | 필수 행이 test/smoke evidence를 갖고 Python runtime dependency와 Node provider duplicate가 남지 않는다 |
| S05 | provider-catalog |
declared provider가 설치·인증됨, 미설치, 미인증, model 미지원 상태 중 하나다 | read-only discovery를 실행한다 | 공식 계열 이름과 내부 id가 정규화되고 실행 불가 상태는 구체적인 오류가 된다 |
| S06 | config-ownership |
agent가 revision A로 실행 중이고 app-owned project override revision B가 저장된다 | watcher가 B를 검증한다 | 현재 agent는 A로 끝나고 다음 호출은 B를 사용하며 invalid B면 새 dispatch가 config error로 중단된다 |
| S07 | selector-policy |
default와 둘 이상의 겹치는 ordered rule이 있다 | Node 또는 Desktop 정책을 평가한다 | 첫 일치 rule의 provider/model 하나만 반환되고 host별 policy 차이가 core evaluator 의미를 바꾸지 않는다 |
| S08 | quota-admission |
서로 다른 project가 같은 provider credential/profile을 사용하고 같은 admission batch에 정상, exhausted, parser-error key가 있다 | quota snapshot과 selection을 실행한다 | 같은 provider key는 app-global quota와 snapshot id를 공유하고 정상/exhausted/unknown은 key별로 격리된다 |
| S09 | workspace-registry |
같은 repository의 clone/worktree 두 경로와 다른 project가 등록됐다 | auto-run을 동시에 시작한다 | 각 canonical workspace instance가 독립 queue/log/session identity로 병렬 실행된다 |
| S10 | task-order |
project A는 기존 active agent-task가 있고 project B는 기존 task가 없지만 ready priority item이 있다 | scheduler가 work를 고른다 | A는 active task를, B는 최상위 ready Milestone을 선택하고 blocked dependency는 건너뛰며 일반 요청에서 새 작업 파일을 만들지 않는다 |
| S11 | concurrency-stop |
여러 project가 병렬 실행 중이다 | 한 project를 사용자가 중단한다 | 해당 process group/session과 후속 auto-call만 멈추고 다른 project는 계속된다 |
| S12 | session-recovery |
provider process 또는 Desktop host가 실행 중 비정상 종료됐다 | host를 재시작한다 | filesystem/checkpoint/locator로 현재 stage를 복원하고 native resume 또는 logical transfer를 한 번만 수행한다 |
| S13 | stream-observer |
provider가 stdout/stderr, delta, heartbeat 공백과 terminal을 발생시킨다 | common runtime이 관측한다 | raw/normalized 순서, heartbeat/inspection, process/session locator와 정확히 한 terminal event가 남는다 |
| S14 | failure-taxonomy |
Python 관측 fixture와 Go provider fixture가 각 known failure를 재현한다 | failure classifier를 실행한다 | context/quota/model/connection/stream/generic/process/review-control class와 evidence source가 일치한다 |
| S15 | project-logs |
실행이 select, run, retry/failover와 terminal 단계를 거친다 | project log를 조회한다 | 최소 관측 필드가 하나의 execution/work-unit identity로 연결되고 secret은 없다 |
| S16 | surface-errors |
invalid config/provider/auth/model/capability 또는 unknown 오류가 발생한다 | Node와 Desktop에서 실행한다 | 양쪽 surface와 project log에 오류가 보이고 unknown은 silent retry/fallback하지 않는다 |
| S17 | desktop-host |
둘 이상의 project가 Desktop registry에 명시 등록됐다 | Edge와 Python 없이 app을 시작·중단·재시작한다 | common AgentTaskManager가 각 project lifecycle을 관리하고 자동 실행 toggle을 지킨다 |
| S18 | flutter-shell |
clean macOS 사용자 환경에 app bundle이 있다 | 설치·실행·창 닫기·명시 Quit·login-start 설정을 확인한다 | Flutter shell과 Go runtime이 D01의 background lifecycle을 지키고 명시 Quit 뒤 orphan process가 없다 |
| S19 | yaml-operations |
UI 편집 화면 없이 app-owned provider/global/project override 설정을 운영한다 | discovery 기반 생성, validation, reload와 조회를 수행한다 | 같은 config service/schema가 roundtrip되고 향후 Flutter UI가 재사용 가능한 scaffold가 있다 |
| S20 | logged-in-smoke |
실제 로그인된 지원 CLI와 macOS app bundle이 준비됐다 | discovery, run, stream, quota/status, cancel, config-next-call smoke를 실행한다 | provider/model/environment/result/evidence 경로가 smoke manifest와 project log에 남는다 |
| S21 | workflow-ownership-boundary |
등록 project에 기존 agent-ops work item은 없고 일반 코딩 요청만 있다 | AgentTaskManager auto-run이 작업을 탐색한다 | no-work/idle로 남고 Milestone/Plan을 분류·생성하지 않으며 별도 오케스트레이션이 WorkRequest를 제공해야 한다 |
Evidence Map
| Scenario | Required Evidence | agent-task 연결 |
완료 Evidence 기대 |
|---|---|---|---|
| S01 | shared provider conformance test와 duplicate implementation search | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
provider-runtime Roadmap Completion, Node/Desktop test output |
| S02 | deterministic scheduler integration test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
agent-task-manager Roadmap Completion, no-supervisor execution trace |
| S03 | Node wire adapter와 Desktop host contract test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
host-boundary Roadmap Completion, event mapping evidence |
| S04 | frozen parity matrix, code search와 cutover verification | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
parity-cutover Roadmap Completion, matrix/evidence links |
| S05 | provider discovery table tests와 authenticated readiness smoke | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
provider-catalog Roadmap Completion, discovery/error output |
| S06 | config merge/watcher/revision integration test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
config-ownership Roadmap Completion, A/B revision trace |
| S07 | ordered selector rule matrix for Node/Desktop policies | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
selector-policy Roadmap Completion, selected rule/reason output |
| S08 | Go usage parser and batch isolation tests | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
quota-admission Roadmap Completion, snapshot id/error isolation evidence |
| S09 | multi-project and same-repo clone integration test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
workspace-registry Roadmap Completion, independent identity/log evidence |
| S10 | remaining-task/priority/dependency scheduler matrix | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
task-order Roadmap Completion, selected work trace |
| S11 | concurrent project cancellation and orphan-process test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
concurrency-stop Roadmap Completion, unaffected project trace |
| S12 | crash/restart native-resume and logical-transfer test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
session-recovery Roadmap Completion, single-resume evidence |
| S13 | fake emitter tests plus actual CLI stream/cancel smoke | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
stream-observer Roadmap Completion, ordered events/single terminal evidence |
| S14 | Python reference fixture inventory and Go typed failure tests | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
failure-taxonomy Roadmap Completion, parity table |
| S15 | project-log schema/golden test and secret scan | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
project-logs Roadmap Completion, trace and redaction evidence |
| S16 | Node/Desktop error matrix integration test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
surface-errors Roadmap Completion, surfaced terminal/log evidence |
| S17 | Desktop multi-project lifecycle integration test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
desktop-host Roadmap Completion, Edge/Python absence evidence |
| S18 | macOS app bundle install/window-close/quit/login-start smoke | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
flutter-shell Roadmap Completion, bundle contents, lifecycle와 orphan check |
| S19 | YAML/discovery roundtrip and config service test | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
yaml-operations Roadmap Completion, schema/reload evidence |
| S20 | actual logged-in macOS field-smoke manifest | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
logged-in-smoke Roadmap Completion, environment/result/evidence manifest |
| S21 | workflow ownership contract test와 common package dependency scan | agent-task/m-shared-agent-task-runtime-desktop-agent/... |
workflow-ownership-boundary Roadmap Completion, no-work/no-artifact trace |
Cross-repo Dependencies
- 없음. 같은 IOP monorepo 안에서 공통 package, Node host, Desktop host와 Flutter shell을 함께 관리한다.
- 구현 순서 선행 조건은 Agent Task 동적 실행 Target Selector, Pi CLI Provider Integration, CLI Agent Group Grade Routing의 활성 Milestone 결과다.
Drift Check
- Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- Evidence Map이 code-review/complete.log에서 검증 가능하다.
- agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- 사용자 리뷰가 필요한 항목은 USER_REVIEW.md에만 남겼다.
사용자 리뷰 이력
- 없음
작업 컨텍스트
- 표준선:
packages/go/agentruntime이 provider와 AgentTaskManager의 유일한 구현이 되고, Node는 기존 runtime wire bridge, Desktop은 app lifecycle/registry/local IPC host가 된다. Desktop은 Flutter가 Go sidecar를 관리하는 topology를 기본안으로 삼는다. - 표준선: app-owned YAML config tree가 provider/global 설정과 registry id별 project override를 모두 소유한다. map/scalar는 project override가 덮어쓰고 ordered selection rule array는 전체 교체한다. workspace-local runtime YAML은 권위가 아니다. 실행 중 agent는 immutable config revision으로 끝나며 다음 호출에만 새 revision을 적용한다.
- 표준선: project task/roadmap/work-log/log가 durable source of truth이고 app store는 provider/global config, registry와 최소 checkpoint만 소유한다. canonical workspace instance가 clone/worktree/branch 병렬성의 identity 경계다.
- 표준선: 동등성 inventory는 구현 계획 직전에 active Milestone과 work log를 다시 읽어 freeze한다. Python behavior는 test fixture/evidence로 변환하되 production dependency로 남기지 않는다.
- 표준선: 실제 로그인 smoke는 fake/unit test를 대체하지 않고 release acceptance evidence로 추가한다. 인증 정보는 기록하지 않는다. Desktop은 official provider/model/profile naming만 사용자 표면에 사용한다.
- 표준선: 등록 project의 agent-ops가 Milestone·Plan·Code Review skill/rule과 작업 파일 의미를 소유하고, 공통 runtime은 이미 선택된 work step의 scheduling/provider execution만 소유한다. 일반 요청 분류와 IOP 소유 skill/tool-call 생성은 별도 에이전트 작업 루프 오케스트레이션 MVP가 이 runtime을 소비해 수행한다.
- 후속 SDD: 완성형 Flutter 설정 UI, Windows/Linux package, 외부 알림/dashboard 또는 remote control을 별도 Milestone으로 올릴 때 작성한다.