Plan — infra-orchestrator v1
infra-orchestrator 개발기획서 (v1)
status: SSOT (초안 v4를 로컬·범용으로 개정)
엔진 repo:infra-orchestrator
CLI:porch(충돌 시infra-orch)
Python 패키지:infra_orchestrator
naming SSOT:docs/NAMING_AND_TOPOLOGY.md
v4 분석: internal/infra-orchestrator-v4-review
한 줄: Python이 상태·검증·Git을 통제하고, Cursor는 Task마다 새로 만드는 로컬 일회성 Agent로만 호출한다.
플랫폼 v1의 Phase 2 “병렬 오케스트레이션”과 slug는 같고 역할은 다르다. 이 repo는 Cursor 개발 Task 자동화다. 제품 스토리보드 병렬 생성은 infra-canvas / ai-engine에 둔다.
다이어그램은 Mermaid다. GitHub, VS Code, Cursor Markdown 미리보기에서 렌더링된다.
목차
- 개요
- 범위와 설계 원칙
- 아키텍처
- 모듈과 Plan 규격
- 데이터 모델
- 상태 머신과 정책
- 사용량
- 신뢰성·보안
- 로드맵
- 테스트·지표·남은 결정
1. 개요
기획서를 입력받아 RELEASE_READY까지 로컬에서 자동 진행하되, Cursor 토큰을 구조적으로 줄이는 Python 엔진이다. 여러 제품 org·workspace가 같은 엔진을 쓰고, 프로젝트 차이는 .orchestrator/만으로 흡수한다.
문제
- 한 Agent 세션에 구현·리뷰·수정·테스트가 쌓이면 입력이 수백만 토큰으로 커진다.
- git status, 테스트, 린트처럼 결정론적 일까지 AI가 한다.
- 기획서 원문이 Task마다 다시 읽힌다.
- Review가 구현 대화에 섞이면 자기 검토 편향이 생긴다.
목표
- 사람 개입 없이 Task 단위로 구현·검증·(필요 시) 리뷰·수정해 RELEASE_READY까지 간다. Merge/배포는 V1 밖.
- Task·Review·Fix마다 새 local Agent. 대화 재사용 없음.
- Git·테스트·린트·타입체크·범위 검사는 AI 토큰 0.
- Task별 사용량을 기록하고 Cost per Verified Task로 이후 분할·모델·중단을 판단한다. V1은 측정만.
- 엔진은 독립 repo. 프로젝트에는 어댑터만.
비목표
- Cursor GUI 매크로. cloud Agent. 무승인 Production. 프롬프트 문구 최적화 도구.
- V1의 병렬 Task, MCP 제어, SQLite, 상태바,
porch release.
성공 게이트
| 게이트 | 기준 |
|---|---|
| G0 | Plan 규격이 실제 기획 1건을 표현한다. FakeAgent 테스트가 통과한다. probe-sdk가 usage·도구 이름을 기록한다. Cost per Verified Task가 수동 대비 개선되거나, 절감 없음이 숫자로 명시된다. |
| G1 | 샌드박스 E2E가 RELEASE_READY. 가능하면 autogen-labs core에 설정만 붙여 소규모 plan 1건. |
G0에서 절감이 없으면 V1 자동 run을 확대하지 않는다.
2. 범위와 설계 원칙
| 구분 | V1 포함 | 제외 (후속) |
|---|---|---|
| 개발 자동화 | 구조화 Plan 컴파일, 순차 Task, 검증, Scope Guard, 체크포인트, 최종 검증 | 비구조 Markdown AI 분해 (V1.5), 교차 리뷰 (V2) |
| 릴리스 | RELEASE_READY에서 정지 | PR/CI/Merge/Deploy (V3) |
| 계측 | usage.jsonl 기록 (observe) | warn/enforce, 과금 정산 |
| 환경 | 엔진 1개 + 프로젝트 .orchestrator/ + repo별 cwd | 거대 모노레포로 합치기 |
원칙 (고정)
- Python이 기본, Cursor는 예외. 정적 분석·테스트·Git으로 알 수 있는 것은 묻지 않는다.
- Task마다 새 Agent, cwd는 대상 repo 하나.
send()정확히 1회.dirs없음. 엔진 repo는 탐색 범위에 넣지 않는다. resume 금지. - 변경되지 않은 기획서만 컴파일한다. 원문 해시가 바뀌면 PLAN_CHANGED. 구조화 Plan은 AI 없이 파싱한다. 기획 대화는 구현으로 넘기지 않는다.
- 검증은 AI 없이, 그리고 먼저. 실패 시에만 stderr 앞부분을 Fix Capsule에 넣는다.
- 불확실하면 멈춘다. 한도·범위 위반·형식 오류 → BLOCKED 또는 HUMAN_REQUIRED.
- 측정 후 개입한다. V1은 observe. 자동 분할·중단은 표본이 쌓인 뒤.
- 정책은 계층이다. defaults + override를 run 시작 시 스냅샷. 안전 키는 강화만.
- 엔진과 프로젝트를 분리한다. 상태는 XDG 상태 홈. 수동 Cursor 명령과 자동 모드는 같은 정책 파일을 공유하되 UI에 의존하지 않는다.
- 최소 권한. 단계별 도구 allowlist. 셸 없음.
run환경에는CURSOR_API_KEY와 최소 PATH만. - 완료는 증거다. Agent의 DONE 선언은 무시한다. AC 매핑 + 검증 + (필요 시) 리뷰 + 체크포인트 SHA가 있어야 DONE.
- 로컬 env 격리. GitHub·배포 비밀은
run프로세스에 넣지 않는다. Agent는 프로세스 env를 상속하므로 시작 전에 걸러진다. - FakeAgent 우선. 오케스트레이터 로직은
cursor-sdk없이 테스트한다. 실 Agent는cursor_client와probe-sdk뒤에만 둔다.
3. 아키텍처
flowchart TB PLAN["PLAN.md"] --> PC["Plan Compiler"] PC --> LOCK["plan.lock.json + ledger.json"] subgraph ENGINE["infra-orchestrator 로컬 프로세스"] PA["Project Adapter · Lock"] SCH["Scheduler"] CB["Context Builder"] AR["cursor_client"] VE["Verifier"] PA --> SCH --> CB --> AR --> VE VE -. 실패 .-> SCH end LOCK --> PA AR --> CUR["cursor-sdk local Agent\ncwd=대상 repo · send 1회"]Run 수명주기
flowchart TD A1["Cursor IDE에서 PLAN.md 작성"] --> B1["porch plan · lint"] B1 --> B2{"사람 승인 N Tasks"} B2 -- 승인 --> B3["Task 루프 구현→검증→리뷰→커밋"] B3 --> B4["최종 검증 AC 커버리지"] B4 --> RR(["RELEASE_READY 정지"]) B2 -- 거절 --> A1 RR -.-> C1["V3 porch release 사람 승인"]porch run은 RELEASE_READY에서 멈춘다. 실행 중 원문 해시가 바뀌면 PLAN_CHANGED.
디렉터리
infra-orchestrator/├── src/infra_orchestrator/│ ├── cli.py│ ├── project.py│ ├── locks.py│ ├── planner.py│ ├── scheduler.py│ ├── context_builder.py│ ├── cursor_client.py # cursor-sdk 유일 import│ └── verifier.py├── prompts/├── schemas/├── policies/defaults.yaml├── examples/sandbox-app/└── tests/
<project>/└── .orchestrator/ ├── project.yaml └── policy.yaml
$XDG_STATE_HOME/infra-orchestrator/projects/<id>/runs/<run-id>/├── run.json├── ledger.json├── events.jsonl├── usage.jsonl└── effective-policy.yaml$XDG_STATE_HOME가 없으면 ~/.local/state/infra-orchestrator.
프로젝트 어댑터 (이것만 복사하지 않고 작성)
<project>/.orchestrator/ project.yaml # id, engine_version, plans.root, repositories policy.yaml # override only- 저장소가 하나여도 1급이다.
checks는 키 → argv 배열. Plan의verify:ID만verify_ids로 선택자에 매핑한다.- 엔진은 Agent
cwd/dirs에 넣지 않는다.
4. 모듈과 Plan 규격
| 모듈 | 책임 | AI |
|---|---|---|
| Project Adapter | 설정 로드, 정책 병합, 스냅샷 | 없음 |
| Lock Manager | 프로젝트·repo 락, dirty tree 거부 | 없음 |
| Plan Compiler | 구조화 Plan → lock + ledger | V1: 0회 |
| Scheduler | 의존성·상태로 다음 Task | 없음 |
| Context Builder | Capsule | 없음 |
| cursor_client | SDK 또는 FakeAgent, usage 정규화 | 호출 시 |
| Verifier | Registry 명령, Scope Guard | 없음 |
Plan 규격 (HTML 주석 금지)
앞머리 orchestrator + Task마다 YAML 펜스 orchestrator. 동반 파일 *.tasks.yaml도 허용한다.
---orchestrator: version: 1 project: example mode: phaserepositories: [sandbox-app]---
# Example
## Decisions| id | decision || --- | --- || D-1 | 공개 API만 변경한다 |
## Phase 1
### DEMO-01
```orchestratorrepo: sandbox-apprisk: lowdepends_on: []decisions: [D-1]```
#### Goal인사 함수가 이름을 반환한다.
#### Acceptance Criteria- AC-1: `greet`가 Hello를 포함한다. (verify: UT-1)
#### Scope- sandbox-app: src/hello.py필수: repo, risk, AC 1개 이상, 각 AC의 verify 또는 review. lint는 순환 의존, 고아, ID 중복, 미등록 repo, 해석 불가 verify ID, 누락 D-id를 오류로 한다. 모호한 AC 문구는 경고.
사람 승인 게이트: lint 통과 후 “N Tasks / M Phases”.
Capsule (상한 가이드, 실측 전 상수 아님)
- Task id, goal, AC
- 참조된 D-id만
- scope expected / forbidden
- 힌트 파일·심볼 (있으면)
- verify id
- 이전 실패 stderr 앞부분 (있을 때만)
- handoff.json (Review/Fix만)
전체 기획서·전체 대화·엔진 소스는 넣지 않는다.
cursor_client (로컬 SDK)
- 패키지
cursor-sdk, Python 3.11+,CURSOR_API_KEY(User 또는 Service Account. Team Admin 불가). 레포에 키를 두지 않는다. LocalAgentOptions(cwd=대상repo). cloud 금지. model 필수. 시작 시Cursor.models.list().- observe/warn:
Agent.prompt()(자동 dispose). enforce(후속):Agent.create+send1회 +wait/cancel+close. CursorAgentError= 미시작.result.status == "error"= 실행 중 실패.- 도구는 호출마다 지정. Review는 읽기 전용. 셸 없음. 잘못된 이름은 생성 에러 → V0에서 표 갱신.
- 설정 소스 기본은 inline.
project는 V0에서 안전할 때만.
검증과 Git
- verify ID →
project.yaml의checks+verify_ids만 실행. argv 배열. - Scope Guard: expected / auto_allowed / needs_approval / forbidden.
- 기존 테스트 파일 삭제·축소는 플래그.
- run 시작 시 dirty이면 거부. repo마다
porch/<run-id>브랜치. VERIFIED 후 Python이 로컬 커밋 (Porch-Task: <id>). push 없음. - hook 실패는 VERIFY_FAILED.
--no-verify금지. 롤백은 run 브랜치 안의 마지막 DONE만.
최종 검증
- Plan 해시
- AC가 verify 또는 review에 매핑되고 PASS. 미매핑은 unverifiable → 사람
final.checks- 미해결 required finding 없음.
discovered_issues는 보고서에만 - 통합 리뷰는 V1 기본 끔 (정책
high_risk_only는 V2) reviewed_shas기록, RELEASE_READY
5. 데이터 모델
계층: Project → Run → Task → AgentCall. 원자적 기록 (temp + rename). V1은 JSON.
정지 상태 (ledger가 재개에 쓰는 값)
TODO · PARTIAL · IMPLEMENTED · VERIFY_FAILED · NEEDS_FIX · VERIFIED · DONE · BLOCKED · HUMAN_REQUIRED
실행 중 표시 (비정상 종료 시 증거로 정지 상태로 복원)
IMPLEMENTING · VERIFYING · REVIEWING · FIXING
판정은 ledger, git diff, 검증 로그, 리뷰 JSON, 체크포인트 SHA. 충돌·재개에서만 읽기 전용 state Agent를 Task당 1회까지 (V2).
run.json 고정값: plan 해시, lock 해시, 정책 해시, engine_version, schema_version, repo별 base SHA. 불일치 → HUMAN_REQUIRED (PLAN_CHANGED / POLICY_CHANGED / BASE_MOVED / ENGINE_MISMATCH).
6. 상태 머신과 정책
stateDiagram-v2 [*] --> TODO TODO --> IMPLEMENTING IMPLEMENTING --> IMPLEMENTED IMPLEMENTING --> HUMAN_REQUIRED IMPLEMENTED --> VERIFYING VERIFYING --> VERIFY_FAILED VERIFY_FAILED --> FIXING VERIFYING --> REVIEWING: MEDIUM_HIGH VERIFYING --> VERIFIED: LOW REVIEWING --> VERIFIED: PASS REVIEWING --> NEEDS_FIX NEEDS_FIX --> FIXING REVIEWING --> HUMAN_REQUIRED: BLOCKED FIXING --> VERIFYING FIXING --> HUMAN_REQUIRED: 한도 VERIFIED --> DONE: checkpointLOW는 AI 리뷰 없음. MEDIUM은 리뷰. HIGH 교차 리뷰는 V2. 수정 후 항상 VERIFYING부터.
기본 모드: phase (Phase 끝나면 승인). 초기 실험은 guided.
정책 병합: 일반 키는 override. risk.force_high_paths는 추가만. agent.tools.*는 축소만, 셸 추가 불가. 잠금 키(sends_per_agent, cwd, dirs, production_credentials, release.create_pr, release.merge, release.production, require_sha_match) 완화는 로드 오류.
잠긴 기본값: local · send 1회 · dirs=[] · shell 없음 · tools 미지정 시 빈 allowlist (SDK 전체 도구 금지) · Plan 셸 미실행 · release.create_pr=disabled · merge/prod 승인 · MCP로 release 없음 · HTML 주석 없음 · XDG 상태 홈 · CLI porch. probe-sdk Agent.prompt는 명시적 sandbox --cwd만. 엔진 repo는 cwd/dirs에 넣지 않는다. Agent env는 allowlist (CURSOR_API_KEY + 최소 PATH).
7. 사용량
V1은 observe. 프롬프트 LLM 사전 분석은 하지 않는다.
우선순위: SDK result.usage / 스트림 → get_usage() → 프록시(호출 수, Capsule 길이, 시간). 없으면 confidence=estimated로 실행은 계속.
최우선 지표: Cost per Verified Task = (실패 포함 총 토큰) ÷ (검증 완료 Task). Context ROI(읽은 파일 / 수정한 파일)는 측정 후 사람 제안만.
8. 신뢰성·보안
| 위험 | 대응 |
|---|---|
| 반쯤 수정된 트리 | Task 전 체크포인트, 실패 시 run 브랜치만 롤백 |
| JSON 형식 오류 | 스키마 → 1회 재요청 → BLOCKED |
| 루프 | max_fix_cycles 3, max_review_cycles 4, max_agent_calls 6 |
| Plan 명령 주입 | Registry만 실행 |
| env 상속 | run에 배포 비밀 없음 |
| 동시 run | 락, 못 잡으면 거부 (대기 없음) |
| dirty 작업 트리 | 시작 거부 |
| SDK 스키마 변경 | cursor_client 격리, 모르는 필드 무시 |
멱등: 단계 시작 전 ledger·git으로 완료 여부를 본다. resume은 마지막 기록된 정지 상태에서만.
9. 로드맵
| 단계 | 범위 | 게이트 |
|---|---|---|
| V0 | 스키마, FakeAgent, porch doctor / probe-sdk, 토큰 비교 3–5 Task | G0 |
| V1 | 순차 MVP, JSON 상태, Scope Guard, 체크포인트, RELEASE_READY | G1 |
| V1.5 | 일반 Markdown 1회 분해, 두 번째 프로젝트는 설정만 | G2 |
| V2 | 위험도, Fix 루프, usage warn | G3 |
| V2.5+ | cross-repo, release, MCP, 병렬 | 필요 확인 후 |
V0 체크리스트: 구조화 기획 1건 수작업 컴파일, Registry 해석, uv sync + CURSOR_API_KEY, Cursor.models.list(), 읽기 전용 Agent로 usage/allowlist/setting_sources/대화 기록 위치 확인, prompt vs create+send 비교.
10. 테스트·지표·남은 결정
- 단위: 상태 전이, Scope Guard, Plan lint, 정책 병합(잠금 키), 락, run 고정값.
- FakeAgent: PASS / NEEDS_FIX / BLOCKED / 형식 오류 / 중도 종료.
- 복구: 전이 직전·직후 강제 종료 후 resume.
- 실 Cursor: 샌드박스, 비용 있으므로 릴리스 전.
지표: Cost per Verified Task, Task당 토큰, 단일 호출 최대, Context ROI, AI 회피율, AC 커버리지, 재작업률, 사람 개입률, 범위 위반율.
V0에만 남는 결정: usage 필드 실측, 실제 도구 이름, setting_sources=project 여부, 고정 model id.
변경 이력
| 버전 | 내용 |
|---|---|
| v1 G0 isolation | tools allowlist → SDK AgentOptions, 기본 도구 집합 금지, Agent env allowlist, probe-sdk는 명시적 sandbox cwd, release.create_pr=disabled 잠금 |
| v1 (이 문서) | slug infra-orchestrator, 로컬 SDK, FakeAgent, YAML 펜스, 단일 상태 enum, V1에서 릴리스/MCP 제거, 범용 .orchestrator/ |
| 초안 v4 | 분석만 internal 리뷰 |