콘텐츠로 이동

Bluetape Skills Part 3: 중단된 workflow를 이어서 복구하는 방법

어두운 작업대에서 로봇 개발자가 멈춘 workflow lane의 receipt를 확인하고 파란 recovery bridge를 연결하는 3D 일러스트
중단된 workflow는 완료 보고가 아니라 남아 있는 상태와 증거를 바탕으로 이어 간다.

Part 2에서는 $bluetape-workflow가 작업을 분류하고, 승인된 계획을 Action·Evidence·Failure checklist로 검증하는 과정을 설명했다. 이 방식은 빠뜨린 gate를 찾는 데 효과적이지만, 작업이 길어지고 여러 native subagent가 나뉘어 움직이면 checklist만으로 답하기 어려운 질문이 생긴다.

한 agent가 한동안 응답하지 않는다면 계산이 오래 걸리는 중일까, 아니면 멈춘 걸까? 새 세션이 작업을 이어받을 때 이전 agent의 결과를 어디까지 믿어야 할까? 일부 lane이 끝났다는 보고만으로 전체 작업을 완료해도 될까? 작업 기록인 receipt가 손상됐다면 멀쩡해 보이는 마지막 줄부터 다시 시작해도 될까?

Bluetape Skills 1.1.0은 2026년 7월 14일 공개됐으며, 이 질문을 다루기 위해 Phase 2 native workflow runtime을 추가했다. 핵심은 새 orchestrator를 하나 더 두는 것이 아니다. Codex main session이 수행한 native coordination을 검증 가능한 상태로 남기고, 중단 뒤에도 그 상태를 읽어 이어 갈지, lane을 교체할지, 별도 recovery run을 만들지 판단하는 데 있다.

왜 복구 가능한 workflow가 필요했나

섹션 제목: “왜 복구 가능한 workflow가 필요했나”

작업이 한 세션에서 끝날 때는 대화 내용만으로도 진행 상황을 대략 파악할 수 있다. 하지만 작업 시간이 길어지고 lane이 여러 개로 갈라지면 설명과 실제 상태가 쉽게 어긋난다. agent가 “완료했다”고 말했어도 required check가 실행되지 않았을 수 있고, 파일은 바뀌었지만 그 파일이 승인된 write scope 안에 있는지 확인하지 않았을 수도 있다.

침묵을 곧바로 실패로 판단하는 것도 위험하다. 큰 빌드나 외부 응답을 기다리는 동안에는 새 메시지가 없어도 정상적으로 작업 중일 수 있다. 반대로 heartbeat가 계속 들어온다는 이유만으로 진척이 있다고 볼 수도 없다. 살아 있다는 신호와 완료에 가까워졌다는 증거는 서로 다른 정보다.

교체 과정에는 더 까다로운 문제가 있다. 멈춘 agent를 새 agent로 바꾼 뒤 이전 agent가 늦게 결과를 보내면, 그 결과가 현재 상태를 덮어쓰지 못하도록 막아야 한다. 세션을 넘길 때도 이전 owner와 새 owner가 동시에 같은 run을 수정하면 안 된다. 어느 쪽이 현재 기록 권한을 가졌는지 분명해야 한다.

그래서 복구 가능한 workflow에는 단순한 작업 목록보다 더 많은 정보가 필요하다. run과 lane의 현재 상태, owner의 세대, liveness 관찰, 교체 관계, component별 check와 evidence, receipt의 검증 결과가 함께 남아야 한다. 그래야 다음 세션이 과거 대화를 추측하지 않고 현재 상태에서 다시 시작할 수 있다.

문서형 checklist에서 native runtime으로

섹션 제목: “문서형 checklist에서 native runtime으로”

1.0.0의 checklist contract는 작업마다 수행할 Action, 남겨야 할 Evidence, 실패했을 때 멈출 조건을 정의했다. 1.1.0은 이 원칙을 없애지 않는다. 오히려 여러 lane이 움직이는 동안에도 같은 원칙을 적용할 수 있도록 상태 기록 계층을 더했다.

공개 bundle에는 manifest version 1.1.0, liveness contract, topology contract, receipt 검증 규칙과 guarded CLI가 함께 들어 있다. manifest는 run과 lane의 상태, 관찰 주기, 교체 횟수, 완료 조건을 고정한다. contract는 각 상태를 어떤 명령으로 바꿀 수 있는지와 그때 필요한 증거를 정의한다.

여기서 runtime은 작업을 대신 실행하는 scheduler가 아니다. .bluetape 아래의 workflow state를 기록하고 검증하는 계층이다. native subagent를 만들고 메시지를 보내며 기다리거나 중단하는 동작은 여전히 Codex main session이 맡는다. Runtime이 담당하는 일은 main session이 실행 전에 남긴 intent와 실행 뒤 관찰한 bounded evidence를 정해진 상태 전이로 기록하는 것이다.

이 구분 덕분에 “명령을 기록했다”와 “native action이 실제로 실행됐다”를 혼동하지 않는다. CLI가 lane-start를 기록했다고 agent가 생성된 것은 아니다. main session이 native spawn을 수행하고 그 결과를 직접 읽은 뒤에야 startup-ack을 남길 수 있다. 침묵은 ACK가 아니다.

run은 승인된 전체 작업을 나타내고, lane은 그 안에서 한 agent나 main session이 맡은 제한된 작업 단위다. 아래 그림의 왼쪽은 하나의 run이 여러 lane으로 작업을 나누고 결과를 모으는 구조를 보여 준다. 오른쪽에서는 lane 하나가 정상 실행과 복구·교체를 거치는 흐름을 따라간다.

하나의 run이 write-ko, write-en, verify-site lane으로 작업을 나누고 required check, component evidence, main verification을 모아 완료되며, 정체된 lane은 별도 ID와 lineage를 가진 replacement lane으로 교체되는 구조
Run은 승인된 작업 전체의 결과를 책임지고, lane은 그 안에서 범위가 제한된 작업 하나를 맡는다. Lane은 병렬 또는 순차로 실행할 수 있지만, terminal state에 도달했다고 run까지 끝난 것은 아니다. Required check, component evidence, main verification이 모두 있어야 run을 완료할 수 있다. 교체할 때는 기존 lane을 재사용하지 않고 새 lane과 agent id, parent lineage를 기록하며, v1.1.0은 lane당 교체를 한 번으로 제한한다.

기본 시작 흐름은 다음과 같다.

bluetape-flow.py: run-approve -> run-start -> lane-create -> lane-start
Codex main session: native spawn
bluetape-flow.py: startup-ack

화살표가 모두 같은 실행기 안에 있는 것처럼 읽으면 안 된다. run-approve부터 lane-start까지는 bluetape-flow.py가 승인과 실행 intent를 기록한다. 그다음 main session이 native agent tool로 agent를 생성한다. startup-ack은 main session이 실제 native 결과를 관찰한 뒤 그 결과를 제한된 evidence로 기록하는 단계다.

lane은 pending, starting, running, suspected_stall, recovering 같은 상태를 거쳐 terminal state로 간다. terminal state는 completed, failed, blocked, cancelled 중 하나다. 교체된 기존 lane에는 replaced가 기록되고, 새 lane은 별도의 lane과 agent id를 사용한다. 기존 lane의 id만 새 agent에게 넘겨 마치 같은 실행이 계속된 것처럼 만들지 않는다.

새 lane은 원래 assignment보다 같거나 좁은 scope와 하나의 checkpoint를 받아야 한다. 기존 lane과의 parent lineage도 기록한다. 교체가 끝난 뒤 이전 agent가 늦게 결과를 보내더라도 current state를 바꿀 수 없도록 fencing하는 이유다. 늦게 도착한 결과가 참고 자료가 될 수는 있어도, 현재 owner의 검증 없이 완료 증거로 승격되지는 않는다.

lane을 완료하기 전에는 main session이 evidence를 다시 읽고, Git status와 branch diff에서 실제 changed path를 수집해 pinned repository root와 write scope 안에 있는지 확인한다. 빈 write scope는 “어디든 써도 된다”가 아니라 “파일을 쓰지 않는다”는 뜻이다. lane-complete를 기록한 뒤에도 required check와 component evidence가 남아 있어야 전체 run의 완료 판정으로 넘어갈 수 있다.

Manifest의 liveness 숫자는 복구 판단에 필요한 시간 경계를 제공한다.

항목v1.1.0 기준의미
관찰 간격30초active native subagent 상태를 다시 확인하는 기본 간격
suspected stall120초유효한 silence lease 없이 침묵했을 때 stall 후보로 보는 경계
silence lease 최대값600초한 번의 lease가 보장할 수 있는 최대 침묵 시간
probe grace60초suspected stall 뒤 probe 응답을 기다리는 시간

600초는 작업 전체의 제한 시간이 아니다. 빌드나 외부 대기처럼 정당한 침묵이 필요할 때 한 번의 lease가 덮을 수 있는 최대 시간이다. lease를 갱신하려면 새 bounded evidence와 침묵이 필요한 이유를 함께 남겨야 한다. “아직 하는 중”이라는 같은 문장을 반복해서 lease를 무기한 늘릴 수는 없다.

120초가 지났다고 runtime이 agent를 곧바로 중단하지도 않는다. liveness-check는 현재 기록을 읽고 다음 행동을 권고하는 read-only 판단이다. suspected stall이라면 stall-record를 남기고, probe-sent를 기록한 뒤 main session이 native send_message 또는 agent 목록 확인을 수행한다. Probe grace 60초 안에 응답이 없고 policy가 interrupt authority를 반환했을 때만 main session이 native interrupt를 수행한다.

복구 흐름을 명령 이름만 줄이면 다음과 같다.

bluetape-flow.py: liveness-check -> stall-record -> probe-sent
Codex main session: native probe -> native interrupt (authority가 있을 때)
bluetape-flow.py: interrupt-result -> lane-reassign

여기서도 Python과 native tool의 경계를 지킨다. probe-sent는 probe를 보낼 intent를 먼저 기록한다. 실제 메시지 전송과 중단은 main session이 수행한다. CLI는 관찰한 결과를 interrupt-result로 남긴 뒤에만 새 lane을 예약하는 lane-reassign으로 이동한다. 한 lane의 교체는 최대 한 번으로 제한된다.

heartbeat는 이 과정에서 agent가 살아 있음을 보여 줄 뿐이다. changed path, check 결과, component coverage, main verification이나 completion evidence가 아니다. 완료 판정에 heartbeat를 재사용하면 “응답 가능한 agent”와 “검증을 마친 작업”을 같은 상태로 취급하게 된다.

가장 약한 required component가 완료를 결정한다

섹션 제목: “가장 약한 required component가 완료를 결정한다”

여러 lane의 완료 보고를 합치는 것만으로는 전체 작업의 완료 여부를 알 수 없다. 한 lane이 여러 component를 맡을 수도 있고, 서로 다른 lane이 만든 결과가 하나의 required check에서 만날 수도 있기 때문이다. 그래서 runtime은 승인된 topology를 component 단위로 기록한다.

각 component에는 다음 정보가 있다.

  • required: 이 component가 전체 완료를 막는 필수 항목인지 표시한다.
  • owner_lane: evidence를 책임지는 lane을 가리킨다.
  • required_checks: 통과해야 할 check 목록이다.
  • dependencies: 먼저 완료돼야 할 component id 목록이다.
  • evidence_refs: receipt에서 확인할 수 있는 제한된 evidence 참조다.
  • coverage_state: 현재 evidence가 component를 덮는지 나타낸다.

완료 규칙은 weakest_required_component다. 전체 run은 가장 덜 완료된 required component보다 앞서갈 수 없다. 예를 들어 글 작성 lane과 site 검증 lane이 모두 completed라고 보고했어도, 한국어 route의 build check가 실패했거나 Hero evidence가 비어 있다면 전체 run은 완료할 수 없다.

Required componentOwner laneRequired checkEvidence판정
Korean articlewrite-kosource-link check있음완료 가능
English articlewrite-enlocale parity있음완료 가능
Rendered routesverify-siteproduction build없음완료 불가

이 예제에서는 앞의 두 component가 준비돼도 Rendered routes가 가장 약하다. completion-check는 terminal lane, required component, check, replacement lineage, evidence와 main verification 중 빠진 항목을 찾는다. 누락이 하나라도 있으면 complete는 실행되지 않는다.

topology에서 component를 없애는 동작도 명시적으로 기록한다. 완료하기 어려워졌다는 이유로 required component를 목록에서 조용히 빼면 안 된다. topology-remove는 제거 이유를 요구하며, 다른 component가 의존하는 항목이나 아직 필요한 required component를 암묵적으로 삭제하지 못하게 막는다.

receipt로 중단된 작업을 복구한다

섹션 제목: “receipt로 중단된 작업을 복구한다”

중단된 run을 복구할 때는 receipt가 정상인지부터 구분해야 한다. Receipt가 정상인 run을 새 세션이 이어받는 경로와, receipt chain이 손상된 run을 복구하는 경로는 서로 다르다.

정상 receipt는 새 owner epoch로 이어 간다

섹션 제목: “정상 receipt는 새 owner epoch로 이어 간다”

먼저 read-only 명령인 resume-check로 run을 replay한다. 미완료 replacement reservation이 있다면 그대로 소유권을 넘기지 않고 replacement-repair로 새 lane을 완성하거나 replacement-block으로 명시적으로 막아야 한다.

검사가 통과하면 resume이 새 owner handle을 만들고 run_resumed event를 추가하며 owner epoch를 1 올린다. 이전 handle은 fencing되고 제거된다. 새 세션이 이전 owner의 credential을 재사용하는 방식이 아니다. 이전 owner와 agent는 최신 epoch의 state를 수정할 수 없다.

owner credential은 command argument나 JSON에 넣지 않는다. 허용된 state root 안의 권한 0600 owner file에만 둔다. resume은 이 파일을 새로 발급하는 소유권 이전이지, 대화에서 token을 복사하는 handoff가 아니다.

손상된 receipt는 별도 recovery run으로 옮긴다

섹션 제목: “손상된 receipt는 별도 recovery run으로 옮긴다”

receipt가 손상됐다면 마지막으로 읽히는 event까지만 남기고 원래 chain을 계속 쓰고 싶을 수 있다. 하지만 그렇게 하면 어디까지가 원래 기록이고 어디부터가 수동으로 이어 붙인 기록인지 증명할 수 없다.

receipt-diagnose는 receipt를 고치지 않는 read-only 진단이다. 첫 번째 손상 sequence, 신뢰할 수 있는 prefix의 head, manifest identity, lock 상태와 손상된 receipt hash를 보고한다. 이 결과로 손상 범위를 확인하되 원래 chain을 truncate하거나 편집하거나 이어 쓰지 않는다.

복구가 승인되면 recovery-run-create가 diagnosis checksum과 승인 evidence를 검증한다. 손상된 원본은 quarantine/ 아래 권한 0600의 immutable copy로 보존하고, provenance를 첫 event에 담은 별도 run을 만든다. 새 run은 기존 chain의 다음 sequence가 아니라 독립된 planned run이다. In-place migration은 지원하지 않는다.

이 구분은 복구를 조금 번거롭게 만든다. 대신 정상 run의 소유권 이전과 손상된 기록의 재시작을 같은 동작으로 섞지 않는다. receipt가 건강하면 epoch를 올려 이어 가고, 손상됐으면 진단 결과와 원본을 보존한 채 새 run으로 옮긴다.

Codex main session이 native agent tool을 직접 실행하고 guarded evidence runtime을 통해 intent와 observed evidence를 manifest, receipt, topology에 기록하는 책임 경계
실행은 Codex main session이 맡고, runtime은 그 실행의 의도와 관찰 결과를 제한된 증거로 남긴다.

bluetape-flow.py는 Python으로 작성됐지만 native coordination 도구를 호출하지 않는다. Agent spawn, 메시지 전송, 대기, 목록 확인과 중단은 Codex main session의 책임이다. CLI가 이 동작을 대신했다고 설명하면 실제 실행 주체와 receipt 기록 주체가 뒤섞인다.

경계는 “intent를 먼저 기록하고, native action을 실행하고, observed evidence를 나중에 기록한다”로 정리할 수 있다. lane-start 뒤에는 native spawn이 있어야 하고, probe-sent 뒤에는 native probe가 있어야 한다. interrupt-result에는 main session이 관찰한 중단 결과만 제한된 형태로 들어간다. CLI 출력만 보고 native 동작이 일어났다고 추론하지 않는다.

.bluetape state를 기록하는 지원 경로는 guarded CLI인 bluetape-flow.py 하나뿐이다. 문제가 생겼다고 receipt나 manifest state를 editor로 직접 고치는 방식은 지원하지 않는다. read-only 명령도 cache나 receipt를 몰래 수정하지 않는다.

evidence에도 경계가 있다. owner credential, secret, raw prompt, 무제한 tool output과 fencing 정보는 receipt나 report의 일반 JSON field로 받지 않는다. Runtime이 필요한 것은 native 도구의 모든 대화가 아니라 다음 상태 전이를 검증할 수 있는 제한된 관찰 결과다. 이 제한이 있어야 receipt가 session transcript의 복사본이 되지 않고 workflow 상태 원장으로 남는다.

기존 설치를 1.1.0으로 올릴 때는 현재 checkout을 덮어쓰기보다 tag를 별도 디렉터리에 shallow clone해서 검증하는 편이 안전하다.

Terminal window
git clone --branch v1.1.0 --depth 1 https://github.com/bluetape4k/bluetape-skills.git bluetape-skills-v1.1.0
cd bluetape-skills-v1.1.0
./scripts/validate.sh
./scripts/install.sh --force

validate.sh는 공개 bundle 경계, manifest inventory, workflow contract와 함께 배포된 회귀 테스트를 검사한다. 실행에는 Bash, rg, Python 3와 uv가 필요하다. install.sh --force는 기존 canonical skill 디렉터리를 타임스탬프가 붙은 backup으로 옮긴 뒤 새 버전을 설치한다. 로컬에서 skill을 직접 수정했다면 강제 교체 전에 git loggit diff로 변경 내용을 먼저 확인해야 한다.

설치 뒤에는 Codex를 다시 시작해야 새 skill을 인식한다. code-reviewself-audit는 공개 canonical bundle 밖에 있는 external companion skill이다. Code Review 경로나 harness self-audit gate를 사용한다면 별도로 설치해야 한다.

v1.1.0 release candidate는 공개 당시 14개 canonical skill, 133 tests와 151 subtests, 83개 파일의 source/bundle parity 검증을 통과했다고 기록돼 있다. 이 숫자는 release 당시의 검증 결과다. 이 글을 쓰면서 같은 전체 suite와 archive 설치를 다시 실행해 얻은 새 측정값은 아니다.

모든 작업을 Phase 2 runtime으로 기록할 필요는 없다. 한 agent가 짧게 끝내는 문구 수정이나 변경 파일과 검증이 명확한 작은 작업이라면 일반 checklist와 Git diff만으로도 충분하다. Run과 lane, topology를 만드는 비용이 실제 복구 위험보다 커질 수 있다.

반대로 다음 조건이 있다면 runtime이 남기는 상태가 도움이 된다.

  • 한 세션보다 오래 걸리거나 새 세션으로 handoff할 가능성이 크다.
  • 여러 native subagent가 병렬 lane으로 움직인다.
  • agent 침묵과 정상적인 장기 작업을 구분해야 한다.
  • 멈춘 lane을 교체하고 늦게 도착한 이전 결과를 fencing해야 한다.
  • 여러 component의 required check와 evidence를 모아 전체 완료를 판정해야 한다.
  • receipt 손상 뒤 provenance를 보존한 recovery run이 필요하다.

선택 기준은 명령 수가 아니다. 중단 뒤 다음 세션이 대화 내용을 추측하지 않고도 현재 owner, lane 상태, component evidence와 receipt를 읽어 안전한 다음 행동을 고를 수 있는가를 본다. 그 질문에 답하기 어렵다면 runtime을 쓸 이유가 있다. 짧고 경계가 분명한 작업이라면 checklist만으로 끝내는 편이 낫다.

댓글

GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.