megálos 워크플로우 테스트하기
1. 범위 및 비-목표
이 스킬은 AI 코딩 에이전트가 배포 전에 megálos 워크플로우 YAML 파일의 동작을 검증하는 방법을 가르칩니다. 주요 도구는 dry-run CLI (python -m megalos_server.dryrun)로, 실제 LLM 대신 모의 입력 소스를 사용하여 프로덕션 런타임에서 워크플로우를 실행합니다. Dry-run은 "주어진 모의 응답으로 이 워크플로우 단계, 분기, 게이트, 하위 워크플로우 전개 및 종료가 작성자가 의도한 대로 동작하는가?"라는 질문에 답합니다. 빠르고, 오프라인이며, 결정론적입니다. LLM 호출이나 MCP 왕복을 수행하지 않습니다. 실제 megálos 배포에 대한 라이브 실행은 dry-run이 도달할 수 없는 경우(실제 LLM 출력 분기 및 실제 레지스트리 mcp_tool_call 분기)를 위해 제한된 보조 도구로 제공되며, §6(이후 작업에서 초안 작성)에서 다룹니다.
범위 내. 대화형 dry-run 레시피 (§3). 회귀 재현을 위한 스크립트 기반 응답 픽스처 (§4). 로드 시간 교차 워크플로우 오류 — unknown_call_target 및 call_cycle_detected — 및 dry-run이 create_app() 부트스트랩을 통해 이를 표시하는 방법 (§5). 에이전트가 읽어야 하는 종료 코드 및 오류 배너 계약.
범위 밖. 파일별 워크플로우 검증 — 동반 스킬 validating-workflows에서 다룹니다. 워크플로우 작성 — authoring-workflows에서 다룹니다. 도메인 저장소 패키징, Horizon 배포 및 레지스트리 설정 — deploying-workflows에서 다룹니다. 프레임워크 수준 테스트 및 megálos 저장소 자체에 대한 워크플로우 픽스처 기여: pytest/conftest는 이 스킬이 가르치는 도구가 아닙니다. testing-workflows 스킬은 dry-run으로 워크플로우 동작을 검증합니다. 프레임워크 테스트 작성을 별개의 활동으로 megálos 기여자에게 맡기며, 명시적으로 범위 밖입니다. 실제 LLM 호출 — dry-run은 LLM을 호출하지 않습니다. 루프에 실제 LLM이 필요한 작성자는 라이브 실행 도구(§6) 또는 배포된 인스턴스를 사용합니다.
대상 독자. 독자는 워크플로우 작성자를 대신하여 작업하는 AI 코딩 에이전트입니다. Dry-run 디자인은 이 대상을 명시적으로 염두에 둡니다: 작성자는 테스트 엔지니어가 아닙니다. 워크플로우 작성자는 지시문 템플릿이 렌더링된 모습, 게이트 목록, 각 단계의 스키마 피드백을 확인해야 합니다. pytest 케이스를 작성할 필요가 없습니다. 아래의 대화형 및 스크립트 기반 dry-run 레시피는 이 대상을 위해 설계되었습니다.
2. 기본 모드로서의 Dry-run
python -m megalos_server.dryrun은 워크플로우를 로드하고 프로덕션 실행 경로를 통해 구동하며, 각 단계의 모의 "LLM 응답"을 모델 호출 대신 stdin(또는 스크립트 YAML 파일 — §4 참조)에서 읽습니다. Dry-run은 병렬 시뮬레이터가 아닙니다. 모의 입력 소스가 있는 프로덕션 런타임입니다. 라이브 서버가 실행하는 start_workflow, submit_step, 분류, output_schema 검증, 재시도 계산, 분기 선택 및 하위 워크플로우 전개를 dry-run도 동일하게 실행합니다. Dry-run이 수락하는 모든 것은 서버도 수락합니다. dry-run이 부트스트랩 시 거부하는 모든 것은 서버가 시작 시 거부합니다.
Dry-run이 실행하는 항목. 단계 렌더링(배너, 사전 조건, 게이트, 지시문). output_schema 검증 및 재시도 도구(검증 힌트, 남은 재시도 횟수, 예산 소진). 분기 선택 및 분기 기본값 해결. 사전 조건 건너뛰기 감지 — 단계의 precondition:이 런타임에 충족되지 않으면 dry-run은 stdout에 Skipped: <step_id>를 출력하고 진행합니다(§3의 도구 계약 참조, §9의 일반적인 실수 형태 참조). 하위 워크플로우 전개 및 상위 재개. 대상의 상위 디렉토리에 있는 전체 워크플로우 세트에 대한 create_app() 로드 시간 검사(unknown_call_target 및 call_cycle_detected 포함 (§5)).
Dry-run이 실행하지 않는 항목. 실제 LLM 생성 품질. 실제 MCP 레지스트리 왕복(mcp_tool_call 단계는 로드 시간에 스키마 및 구조적 검사로 처리되지만, 실제 레지스트리 호출은 라이브 실행 문제입니다). 프로덕션 규모의 세션 지속성, 지연 시간 또는 동시성. 작성자가 이 중 하나라도 확인해야 하는 경우 라이브 실행 도구가 도구입니다(§6).
종료 코드 계약. Dry-run은 workflow_complete 시 0으로 종료되고, 다른 모든 터미널 상태(_TERMINAL_STATUSES frozenset: {"workflow_complete", "error", "session_escalated", "workflow_changed"}) 시 1로 종료됩니다. 0이 아닌 종료는 항상 stderr에 디코딩된 오류 배너를 수반합니다. Dry-run을 스크립팅할 때(CI 스모크 테스트, pre-commit 검사) 종료 0을 유일한 성공 신호로 처리해야 합니다.
3. 대화형 dry-run 레시피
대화형 모드는 일반적인 경우입니다: 에이전트(또는 그 사람 운영자)가 각 단계에서 모의 LLM 응답을 입력하고 워크플로우가 전개되는 것을 지켜봅니다. 호출:
python -m megalos_server.dryrun workflows/my_workflow.yaml
Dry-run은 대상의 상위 디렉토리에 있는 모든 *.yaml 파일을 로드하고(하위 워크플로우 call: 대상을 확인하는 데 필요 — §5 참조), 워크플로우를 부트스트랩하고 REPL에 진입합니다. 각 단계에서 런타임은 배너, 선택적 사전 조건 줄, 모든 게이트 및 단계의 지시문 템플릿을 렌더링한 다음 > 프롬프트를 표시합니다. 에이전트는 모의 LLM 응답을 입력하고 Enter를 누릅니다. 워크플로우가 진행됩니다.
최소 3단계 선형 워크플로우 세션의 예:
$ python -m megalos_server.dryrun workflows/example.yaml
=== Step: alpha — First step ===
<directive template rendered here>
> ok
=== Step: bravo — Second step ===
<directive template rendered here>
> ok
=== Step: charlie — Third step ===
<directive template rendered here>
> ok
Workflow complete
종료 코드 0. 세 개의 모의 응답이 세 단계를 workflow_complete로 이끌었습니다.
에이전트가 만날 수 있는 프롬프트.
-
단계 응답 프롬프트 (
>). 모든 단계의 기본 프롬프트입니다. 에이전트가 입력하는 모든 것은 LLM이 생성한 것처럼 런타임에 제공됩니다.output_schema가 있는 단계의 경우 응답은 스키마와 일치하는 유효한 JSON이어야 합니다. 잘못된 JSON 페이로드는 검증 오류를 트리거하고(stderr에Retries remaining: N줄과 함께) 동일한 단계에서 다시 프롬프트합니다. 재시도 예산을 소진하면Max retries (N) exceeded배너와 함께1종료 코드로 실행이 종료됩니다. -
분기 선택 프롬프트.
branches:블록이 있는 단계에 도달하면 dry-run은 먼저 단계 응답을 소비한 다음 다음을 출력합니다:Branches: 1. first_branch_target 2. second_branch_target [default] Choose branch [1-2, empty = default]:[default]태그는 선택기가 단계 응답을 평가할 때 선택된 분기를 표시합니다. 빈 입력은 기본값으로 해결됩니다. -
사전 조건 줄.
precondition:이 있는 단계는 지시문 앞에Precondition: <ref> == "<value>"또는Precondition: <ref> is present로 진입 시 렌더링합니다. -
건너뛰기 도구 (
Skipped: <step_id>). 단계의 사전 조건이 런타임에 충족되지 않으면 dry-run은 해당 단계의 배너나 프롬프트를 렌더링하지 않습니다. 대신 stdout에Skipped: <step_id>를 출력하고 다음 단계로 진행합니다. 이전 단계에서 이미 렌더링된Precondition:줄이 암시적 설명입니다. 건너뛰기 감지는 사전 조건이 있는 단계만 표시합니다. 단순히 선택되지 않은 대체 분기는 "건너뛰기"되지 않습니다. 도달하지 않은 경로입니다. 의도하지 않게 단계를 건너뛰는 실행은 여전히 종료0의workflow_complete로 종료되므로, 의도하지 않은 건너뛰기의 유일한 신호는 stdout의Skipped:줄입니다. 모든Skipped:줄을 읽고 해당 단계를 억제한 사전 조건이 실제로 억제하려는 의도였는지 확인하십시오. -
EOF (Ctrl-D). 워크플로우 중간에 stdin을 닫으면 stderr에
Dry-run aborted by user (EOF)가 표시되고 종료 코드1로 중단됩니다.
하위 워크플로우 전개. 단계의 call: 대상이 형제 워크플로우로 확인되면 dry-run은 Entering sub-workflow <name>을 출력하고 중첩 수준당 두 칸 들여쓰기로 자식의 배너를 들여씁니다. 자식의 workflow_complete 시 dry-run은 Returned from sub-workflow를 출력하고 다음 단계에서 상위를 재개합니다.
정리된 디렉토리 규율. dry-run은 상위 디렉토리의 모든 *.yaml을 로드하므로, 깨진 형제 YAML은 대상 로드를 차단합니다. 부트스트랩 시간 오류 배너가 대상이 아닌 다른 파일을 명명하면 형제에 문제가 있는 것입니다. 수정하거나, 대상을 해당 대상과 그 call: 대상만 포함하는 디렉토리로 이동하십시오. dry-run이 로드 실패 시 출력하는 프레이밍 단락은 다음과 같습니다:
Failed to load workflows from <parent_dir>: <exception>
Note: dry-run loads all *.yaml files in the parent directory (required
for sub-workflow 'call' target resolution). If the error above names a
file other than <target.yaml>, a sibling workflow has a problem — fix
it, or move <target.yaml> to a directory containing only it and its
call targets.
4. 스크립트 기반 응답 픽스처
회귀 재현 및 CI 스모크 테스트를 위해 dry-run은 워크플로우를 비대화식으로 구동하는 YAML 픽스처를 가리키는 --responses-file을 허용합니다:
python -m megalos_server.dryrun workflows/my_workflow.yaml \
--responses-file tests/responses/my_workflow_happy.yaml
응답 파일은 고정된 형태를 가집니다: 필수 version: 1 키와 필수 entries: 목록이 있는 최상위 매핑입니다. 각 항목은 필수 step_id:와 정확히 하나의 response:(단계 내용용) 또는 branch:(분기 선택 프롬프트용) — 둘 다이거나 없어서는 안 됩니다. 최소 3단계 happy-path 픽스처:
version: 1
entries:
- step_id: alpha
response: ok
- step_id: bravo
response: ok
- step_id: charlie
response: ok
순서대로 소비. 루트 워크플로우 프레임(전개 깊이 0)에서 항목은 순서대로 소비됩니다: 첫 번째 항목은 첫 번째 단계의 step_id와 일치해야 하고, 두 번째 항목은 두 번째 단계와 일치해야 하는 식입니다. 철자 또는 순서가 어긋나면 실행이 중단됩니다. 하위 워크플로우 전개(깊이 > 0) 중에 dry-run은 일치하는 항목을 찾을 때까지 일치하지 않는 항목을 건너뛰므로, 상위 프레임과 자식 프레임 항목이 파일 순서대로 섞일 수 있습니다. 그러나 각 프레임의 자체 항목은 해당 프레임의 실행 순서대로 나타나야 합니다.
드리프트 감지 배너. 파서 및 스크립트 소비 계층은 종료 1 전에 stderr에 축어적 배너를 출력합니다. 픽스처가 잘못된 경우 작성자는 정확히 다음 문자열을 보게 됩니다:
- 버전 필드 누락:
Responses file missing required 'version' field. Expected: version: 1 - 알 수 없는 버전:
Unknown responses-file version: <n>. Supported: [1] - 응답/분기 상호 배제(둘 다 있음):
... must have exactly one of 'response' or 'branch', not both. - 응답/분기 상호 배제(둘 다 없음):
... must have exactly one of 'response' or 'branch'. - 루트 깊이에서의 Step-id 드리프트:
Script entry expected step_id=<entry_step>, REPL at step_id=<repl_step> - 항목 유형 불일치(단계가 응답을 예상했지만 스크립트가 분기를 제공함):
At step <repl_step>, expected step response but script provided branch selection(반대의 경우:expected branch selection but script provided response) - 소진(항목이 너무 적음):
Responses file exhausted at step <repl_step> (expecting: <expected_label>) - 완료 시 사용되지 않은 항목(항목이 너무 많음):
Responses file had <N> unused entries after workflow completion
사용되지 않은 항목 검사는 실행 종료 직전 workflow_complete 시 실행됩니다. 필요 이상으로 긴 픽스처는 조용히 통과하는 것이 아니라 스크립팅 오류입니다. 모든 드리프트 케이스는 1로 종료되므로, 종료 0을 정상으로 처리하는 CI는 위의 모든 경우에 오류를 발생시킵니다.
대화형 모드와의 범위 동등성. 스크립트 모드는 대화형 모드와 동일한 도구를 구동합니다: output_schema 검증 및 재시도 배너(잘못된 response:는 동일한 검증 오류 재프롬프트를 트리거함), 분기 선택(branch: 항목을 통해), 하위 워크플로우 전개(상위/자식 항목이 섞임). 워크플로우를 처음부터 끝까지 실행하는 스크립트 픽스처는 해당 워크플로우의 표준 회귀 아티팩트입니다.
5. 로드 시간 교차 워크플로우 오류 — unknown_call_target 및 call_cycle_detected
두 가지 교차 워크플로우 오류는 파일별 검증이 아닌 워크플로우 세트 로드 시간에 표시됩니다. 둘 다 validating-workflows 스킬이 순방향 참조로 표시한 동일한 패스에서 방출됩니다:
내재화할 가치가 있는 미묘한 점: 이 두 오류는 워크플로우 세트 로드 시간에 표시되며, 파일별
python -m megalos_server.validate <file>실행 시 표시되지 않습니다. 파일별 검증기는 외부 워크플로우 자체의 구조적 및 의미적 규칙만 다룹니다. 서버가 결국 로드할 다른 워크플로우에 대해 알 수 없습니다. 호출 대상 및 순환 검사는 워크플로우 디렉토리의 모든 워크플로우 YAML이 개별적으로 로드된 후, MCP 앱이 구성되기 전에 서버의create_app()내부에서 실행됩니다. 실제로 이는 다음을 의미합니다:call:단계가 있는 워크플로우를 작성하거나 수정하는 에이전트는unknown_call_target및call_cycle_detected를 표시하기 위해 전체 워크플로우 세트를 서버에 로드하거나,testing-workflows에서 다루는 테스트 도구를 사용해야 합니다. 파일별 검증 실행이 통과하는 것은 서버를 로드할 수 있기 위한 필요 조건이지만 충분 조건은 아닙니다.
이 섹션이 그 테스트 도구입니다. 로더가 방출하는 축어적 f-문자열 형태는 다음과 같습니다:
Workflow '<parent_name>' step '<step_id>' calls unknown workflow '<target>' (code: unknown_call_target)
call cycle detected: <wf_a> -> <wf_b> -> ... -> <wf_a> (code: call_cycle_detected)
Dry-run이 이를 표시하는 방법. Dry-run의 부트스트랩은 create_app(workflow_dir=<target's parent directory>)를 호출합니다. create_app()은 해당 디렉토리의 모든 *.yaml을 로드한 다음, MCP 앱이 구성되기 전에 교차 워크플로우 호출 확인 패스를 실행합니다. call: 대상이 로드된 워크플로우로 확인되지 않거나, 호출 그래프에 순환이 포함된 경우 위의 오류 코드와 함께 로드가 실패하고 dry-run은 REPL이 시작되기 전에 부트스트랩에서 1로 종료됩니다. 실패는 §3("dry-run loads all *.yaml files in the parent directory…")에 표시된 프레이밍 단락으로 감싸져 작성자가 어떤 파일이 관련되었는지 확인할 수 있습니다.
실제적 의미. 파일별 python -m megalos_server.validate <file> 실행은 unknown_call_target 또는 call_cycle_detected를 표시할 수 없습니다. Dry-run 부트스트랩이 유일한 조기 검사 지점입니다. 이 스킬의 문서는 Dry-run을 사용하는 방법을 설명합니다.