문제의 출발점
AI에게 코드를 맡길 때 번거로운 일은 코드 작성만이 아니에요. 지금까지 무엇을 했는지 설명하고, 다음 작업을 정하고, 결과를 확인하는 일도 매번 반복돼요. 대화가 길어지거나 실행이 중단되면 같은 설명과 검사를 다시 해야 할 때도 있어요. 한 번 잘 정리한 개발 과정을 다음 작업에서도 사용할 수 있다면 이 반복을 줄일 수 있을까요?
이 시리즈에서 재사용하려는 것은 목표를 정리하고, 해결 방법을 검토하고, 실행 근거로 결과를 확인하는 절차예요. 프로젝트마다 파일과 요구는 달라지므로 Task 목록은 그 목표에 맞게 다시 만들어야 해요. 같은 프로젝트에서 중단된 실행을 이어 가는 일도 이 절차 안에서 다뤄요.
먼저 떠올릴 수 있는 방법은 작업 목록을 저장하고 하나씩 실행하는 것이에요. 다음 일을 고르고, AI가 수행한 결과를 검사하고, 상태를 갱신해요. 이 흐름을 반복하면 중단된 작업도 목록의 다음 항목부터 이어 갈 수 있을 것 같아요.
하지만 목록을 저장하는 것과 개발 과정을 재사용하는 것은 다른 문제예요. 어떤 일을 목록에 넣을지, 무엇을 먼저 할지, 무엇으로 완료를 확인할지는 프로젝트마다 다시 정해야 해요. 이 시리즈는 VibeMarshal의 구현을 따라가며 그 설계까지 AI에게 맡기려면 무엇이 필요한지 살펴봐요.
실행을 반복하는 루프에서 시작하기
작업 목록이 이미 올바르게 주어졌다고 가정해 볼게요. 시스템이 해야 할 일은 비교적 분명해요.
작업과 상태 저장 → 실행할 작업 선택 → AI 실행
↓
다음 작업으로 반복 ← 상태 갱신 ← 결과 검사중단된 지점을 기록하면 처음부터 다시 시작하는 일을 줄일 수 있어요. 작업에 검사 조건을 붙이면 AI의 완료 선언과 실제 결과도 비교할 수 있어요. 그런데 이 루프의 출발점에는 중요한 가정이 있어요. 실행할 작업과 순서, 완료 조건이 이미 잘 설계돼 있어야 해요. 잘못 만든 목록을 정확히 실행하는 것으로는 원래 문제를 해결할 수 없어요.
VibeMarshal은 요청을 목표와 계획으로 정리하고 실행과 검사 근거를 기록하는 로컬 Workflow Orchestrator예요. 현재 구현의 Python namespace는 flowmarshal, 패키지와 CLI 이름은 flowmarshal-engine이에요. 설명은 고정 커밋 32bb0f9를 기준으로 해요. 패키지는 0.2.0a1 프리릴리스이며 실제 제품 검증 범위는 마지막 편과 공통 부록에서 구분해요.
저장소에서 실제 실행 경로 찾기
저장소에는 현재 Engine뿐 아니라 이전 실험과 GUI, 평가 도구도 함께 있어요. 이 구분을 하지 않으면 화면의 동작이나 과거 평가 결과를 현재 제품의 기능으로 읽기 쉬워요.
| 영역 | 읽어야 할 내용 |
|---|---|
src/flowmarshal/engine | 목표·계획·원장·실행·검증·복구의 현재 구현 |
src/flowmarshal/core, planning, gate0b, gate0c | 이전 Core와 Planner 및 경계 검증 실험 |
apps/desktop | React·TypeScript GUI 프로토타입 |
tests, config | 오류 경로와 fixture, 역할 설정 및 제품 검증 조건 |
docs, spikes, scripts | 설계 계약, 과거 실험 기록과 설치 후보 검사 |
현재 실행 경로를 이해할 때 중심이 되는 것은 flowmarshal.engine이에요. setup.py는 이전 구현과 개발 평가용 모듈을 사용자 wheel에서 제외해요. GUI의 App.tsx는 MockEngineClient를 생성하므로 GUI 화면을 실제 Engine 실행의 증거로 볼 수는 없어요. 패키징 경계, GUI 진입점
두 함수만 구현하는 작은 요청
시리즈 전체에서 같은 계산 사례를 따라가 볼게요. 저장소의 project-e2e-multi-task fixture에는 두 함수와 검사가 있어요. fixture는 검증을 위한 작은 예제 프로젝트예요. 시작 상태에는 공개 함수만 있고 구현은 없어요.
# money.py
def cents(amount: str) -> int:
raise NotImplementedError
# invoice.py
from money import cents
def total(prices: list[str]) -> int:
raise NotImplementedErrorcents는 금액 문자열을 센트 단위 정수로 바꾸고, total은 가격 목록의 합계를 구해야 해요. 여기에는 반환값 외에도 조건이 있어요. total은 기존 cents를 사용해야 해요. 테스트에는 그 호출을 확인하는 검사도 들어 있어요. 변환 함수, 합계 검사
설명용 요청에서는 함수 이름과 인자를 유지하고, 표준 라이브러리만 사용하며, 테스트 파일을 바꾸지 않기로 할게요. 허용하는 행동은 두 구현 파일의 수정과 로컬 검사예요. 외부 push와 배포는 허용하지 않는다고 가정해요. 마지막 승인 조건은 사례를 설명하기 위해 추가한 것이며 fixture 자체의 지침은 아니에요.
작업 목록은 간단해 보여요. 먼저 cents를 구현하고 검사해요. 그다음 total을 구현하고 검사해요. 마지막으로 두 함수를 함께 확인해요. 하지만 이 목록만으로는 테스트를 고쳐서 통과시켜도 되는지, 합계 함수가 자체 변환 코드를 써도 되는지 알 수 없어요. 작업 이름보다 더 오래 유지해야 할 기준이 필요해요.
상태를 저장할 때 무엇을 함께 남겨야 할까요
Engine은 목표인 Goal, 실행 계획인 Plan, 실제 실행 시도인 Attempt를 나누어 기록해요. 작업 상태와 검사 결과뿐 아니라 어떤 입력과 실행에 연결된 결과인지도 남겨요. SQLite 원장은 foreign key와 transaction으로 내부 상태를 관리하고, History와 evidence 등의 수정·삭제를 제한해 과거 근거를 보존해요. domain 정의, 원장 구현
이 구분은 ‘완료’ 표식만 저장하는 것보다 많은 정보를 요구해요. 대신 파일이나 목표가 바뀌었을 때 과거 결과를 그대로 사용할 수 있는지 판단할 근거가 생겨요. 같은 프로젝트에서 작업을 이어 가는 데 필요한 기록과, 다른 프로젝트에 적용할 수 있는 설계 원칙도 구분할 수 있어요. 원장을 복사한다고 새 프로젝트의 목표와 계획까지 맞아지는 것은 아니에요.
실행을 한 번 진행하는 run-once도 같은 구분을 따라요. 한 번의 상태 전이나 RuntimeJob 예약·관측을 처리하고 반환하며, 명령이 반환됐다고 provider 작업이나 전체 Goal이 끝났다는 뜻은 아니에요. 다음 호출은 저장된 상태를 다시 확인해요. 사용자 실행 흐름
여기까지 자동화해도 작업 목록을 설계하는 일은 남아요. 다음 프로젝트에서 재사용할 것은 이전 목록 자체보다 목표를 정리하고, 작업을 나누고, 근거로 결과를 확인하는 과정이에요. 그렇다면 그 목록을 만드는 일도 AI에게 맡길 수 있을까요? 다음 편에서는 AI가 만든 작업을 연결하는 것만으로 원래 요청을 해결할 수 있는지 살펴봐요.