Claude Code의 가벼운 작업을 Codex로 위임하기: 규칙만으로는 0건이었습니다
요약
Claude Code와 Codex CLI를 연동하여 AI 에이전트의 작업 위임(Delegation) 문제를 해결하는 방법을 다룹니다. 단순히 규칙을 설정하는 것만으로는 위임이 일어나지 않았으며, 래퍼 스크립트 사용과 요청 문구 변경 등 세 가지 메커니즘 적용 후 성공했습니다.
핵심 포인트
- 규칙만으로는 작업 위임이 불가능하며, 추가적인 메커니즘이 필요합니다.
- 작업 모드(read/write)를 고정하는 래퍼 스크립트 사용을 권장합니다.
- 요청 문구를 '허락' 형태가 아닌 '넘기는 지시문' 형태로 변경해야 합니다.
Claude Code에 '가벼운 작업은 Codex로 넘겨줘'라고 CLAUDE.md에 요청했음에도 불구하고, 단 한 건도 넘어가지 않았습니다.
혹시 이런 상황에 놓여있지 않으신가요?
제 환경이 바로 그랬습니다.
규칙을 작성한 다음 날 대화 기록을 세어보니, 실무에서 실행된 서브 에이전트 46건 중 Codex로 넘긴 건수는 0건...
여기서부터는 위임이 시작되는 때까지 제가 적용했던 3가지 메커니즘과 테스트를 감사하며 발견한 문제점들입니다⚠️
그 3가지는 바로, 래퍼(Wrapper),
서브 에이전트 정의알리는 것만 하는 hook
입니다. 보안 측면에서 비활성화하지 않은 점도 함께 작성하겠습니다💪
이 글을 읽으면 다음 4가지를 알 수 있습니다👇
- Claude Code에서 Codex CLI를 '읽기 전용(read-only)'과 '작업 디렉터리 쓰기'로 분리하여 호출하는 작은 래퍼 만드는 방법
- CLAUDE.md에 규칙만 작성해서는 위임이 일어나지 않았던 이유와, 요청 문구를 '허락하는' 형태에서 '넘기는(회수하는)' 형태로 바꾼 이야기
- PreToolUse hook에서 막지 않고 알리기만 한 이유
- 위임 여부를 나중에 셀 수 있도록 기록을 남기는 방법
Claude Code와 Codex CLI를 모두 사용하는 분들께 작성했습니다.
서브 에이전트 사용법을 직접 구성하는 사람들을 상정하고 있습니다.
글을 작성한 시점의 버전은 Claude Code 2.1.290, Codex CLI 0.159.2입니다.
결론부터 말씀드립니다
규칙 문구만 추가해서는 위임 건수가 여전히 0이었습니다...
위임이 시작된 것은 다음 3가지를 넣은 후였습니다👇
- 래퍼(
~/.local/bin/codex-sol)를 사용하여 모드, 모델, 금지 사항을 고정함 - 서브 에이전트 정의와 요청 문구에 '위임해도 좋다'가 아니라 'codex-sol로 넘긴다'고 지시문으로 작성 - 조사나 목록 작성이 보이는 서브 에이전트의 실행을 hook에서 부모 Claude에게 알림(막지는 않음)
그 위에, Codex의 답변을 그대로 사용하지 않고 호출한 쪽의 Claude가 차이점이나 검색 결과를 다시 확인하는 약속을 반드시 세트로 하고 있습니다💡
무엇을 Codex로 넘기고, 무엇을 넘기지 않을 것인가
처음에 정한 것은 대상 범위입니다. 전역적인 CLAUDE.md에서 다음과 같이 결정하고 있습니다.
| 넘길 것 | 넘기지 않을 것 |
|---|---|
| 대상 파일・완료 조건・검증 방법이 명확한 검색 | 설계 판단, 사양이 모호한 작업 |
| ... | |
| 단발적인 grep처럼 Claude의 툴로 한 번에 끝낼 수 있는 것은 굳이 넘기지 않습니다🙂↕️ |
호출과 왕복하는 쪽이 시간이 더 걸리기 때문입니다💪
1. 래퍼로 호출 방식을 하나로 고정하기
codex exec의 인수를 매번 Claude가 조합하게 하면, 지정이 흐트러질 걱정이 있습니다.
그래서 호출 방식을 하나의 셸 스크립트로 정리했습니다. 사용법은 이것뿐입니다💡
~/.local/bin/codex-sol read /path/to/project <<'TASK'
목적: 지정한 함수의 호출 원본을 조사해 주세요.
대상: src/example.ts와 src/ 내의 호출 원본만 조사해 주세요.
...
히어 다큐먼트(Here Document)의 구분자를 'TASK'로 따옴표로 감싼 것은, 지시문 안의 $나 역따옴표를 셸이 전개하지 않도록 하기 위함입니다💡
래퍼 내부 내용 (핵심만)
#!/bin/sh
set -eu
case "$1" in
...
정해둔 것은 4가지입니다👇
- 모드는 단 두 가지입니다.
read는--sandbox read-only,write는--sandbox workspace-write에 대응시켰습니다.write를 사용하는 것은 사용자가 승인한 국소 수정과 성과물을 작성하는 검증만 가능합니다 - - 모델과 추론량을 고정했습니다. 가벼운 작업에 한정하므로, 추론량은 low로 설정했습니다 -
- 서두에 금지 사항을 반드시 붙입니다. commit・push・외부 전송・
.env의 읽기/쓰기를 지시문의 맨 앞에서 매번 금지하고 있습니다 - - 작업 디렉터리는 절대 경로만 받습니다. 나중에 작성할 기록에도 그 경로가 그대로 남습니다
이는 Codex가 실행하는 명령어에 사람의 승인을 거치지 않도록 설정한 것입니다.
Claude로부터 비대화적으로 호출하기 때문에, 승인 대기 상태로 멈추지 않도록 이렇게 했습니다💡
이 설정은 다음 두 가지와 함께 사용한다는 전제가 있습니다👇
- 사일로(sandbox)는
read-only또는workspace-write둘 중 하나로 제한됩니다.danger-full-access와 조합하면, 승인도 사일로도 없는 상태에서 명령어가 실행되므로, 저는 이 조합을 사용하지 않습니다 - 앞서 언급한 금지 사항은 Codex에 대한 '요청'입니다. 사일로처럼 강제하는 메커니즘이 아닙니다. 예를 들어.env파일을 읽지 않는 것은 지시문에 의존하고 있습니다
즉, 사일로에서 멈추지 않는 작업은 지시문과 나중에 Claude가 확인하는 절차로만 방지할 수 있습니다⚠️
비밀 값을 다루는 리포지토리에서 write를 사용할 때는 이 전제를 기억한 후 사용해 주세요💡
Codex 측에 흔적이 남지 않도록 래퍼(Wrapper)로 기록하기
--ephemeral을 붙이면, Codex는 세션의 파일을 디스크에 남기지 않습니다.
대화 내용이 남아있지 않은 점은 안심되지만, 곤란한 일도 있었습니다😅
나중에 '위임했는지'를 Codex 측에서 확인하려고 해도 아무것도 나오지 않거든요…
그래서, 래퍼의 마지막에 한 줄만 기록을 추가했습니다💡
작업 지시문의 본문은 남기지 않습니다💪
sol_log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/codex-sol"
if [ -n "${CLAUDECODE:-}" ]; then sol_caller=claude; else sol_caller=other; fi
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s' "$sol_started_at" "$1" "$2" "$sol_status" \
...
열은 시작 일시・read인지 write인지・작업 디렉토리・종료 코드・초 수・호출원・미완료 여부입니다. 실제 행은 이런 형태가 됩니다 (작업 디렉토리는 대체했습니다).
2026-10-06T08:33:29+0900 read /path/to/project 0 29 claude 없음
주의할 점이 하나 있습니다⚠️
종료 코드의 0은 Codex 프로세스가 정상적으로 종료되었음을 의미할 뿐입니다.
요청한 작업을 끝까지 완료했는지는 별도로 확인해야 합니다.
7번째 열의 미완료 여부는 그 때문에 나중에 추가한 열입니다 (이유는 후반부 감사(audit) 부분에서 설명하겠습니다).
2. 규칙만으로는 0건이었다
래퍼를 배치하고 전역적인 CLAUDE.md에 '가벼운 작업은 Codex에 위임합니다'라는 절을 작성한 것이 10월 4일입니다. 다음 날, 대화 기록을 세어보았습니다.
- 규칙이 로드된 후 시작된 서브 에이전트는 26건이었고, 위임은 0건이었습니다
- 조건을 재검토하여 다시 작성하고 다시 세어보니, 실제 업무용 서브 에이전트 46건 중 역시 0건이었습니다
46건의 대부분은 문안 감사나 독립 검토로, 실행하지 않는 것이 올바른 작업이었습니다.
그럼에도 불구하고 약 10건 정도는 대조・목록 만들기・코드 조사에서 위임 조건에 해당했습니다.
가장 의외였던 것은 그중 4건입니다‼️
요청문에 'codex-sol로 위임해도 좋다'라고 명확하게 적혀 있었는데도, 서브 에이전트가 스스로 Grep와 Read를 거쳐 끝내고 있었습니다💡
서브 에이전트에게도 CLAUDE.md는 로드되고 있습니다. 읽은 후에 사용하지 않고 있었습니다‼️
'허용'에서 '실행'으로 변경하기
'해도 좋다(してよい)'는 안 해도 좋다는 의미로도 해석될 수 있거든요…
손안의 툴로 해결할 수 있다면, 서브 에이전트는 그쪽을 선택하는 것 같았습니다🧐
그래서, 요청문과 서브 에이전트 정의의 작성 방식을 지시(instruction) 형태로 바꿨습니다👇
(변경 전)軽い作業は codex-sol へ委譲してよいです。
(변경 후)対象ファイル・完了条件・検証方法が明確な検索、既知のテスト実行、
局所的な修正、機械的な置換は ~/.local/bin/codex-sol に回し、
...
함께, 리포지토리의 .claude/agents/general-purpose.md에도 같은 지시를 작성했습니다.
서브 에이전트 종류를 지정하지 않고 시작하더라도 이 정의가 읽히도록 하기 위해서입니다.
역할별로 일괄적으로 임무를 맡기는 서브 에이전트에는 전용 정의도 두었습니다. 제 환경에서는 이 역할을 '책임자(責任者)'라고 부르며, 정의는 .claude/agents/sekininsha.md에 두고 있습니다. 본문의 '작업 진행 방식'에 다음 지시를 작성했습니다.
대상 파일・완료 조건・검증 방법이 명확한 검색, 알려진 테스트 실행, 국소적인 수정,
기계적인 치환은, 스스로 Grep이나 Read를 반복하기 전에 ~/.local/bin/codex-sol로 보내,
결과를 확인한 후에 사용합니다 (commit과 MCP를 사용하는 작업은 대상에서 제외입니다).
정의 본문에 지시사항을 적어두면, 시작할 때마다 요청문에 같은 문구를 쓸 필요가 없습니다.
요청문에는 어떤 책임자인지, 무엇을 부탁하는지만 쓰면 되어서 편리해졌습니다💡
3. hook은 막지 않고, 알리기만 한다
문구를 바꿔도, 상위 Claude가 서브 에이전트를 시작할 때 '이건 처리 가능한 작업이다'라고 인지하지 못하면 같은 일이 발생합니다.
그래서 Agent 도구의 PreToolUse에 hook을 하나 추가했습니다👇
hook의 판별은 이것뿐입니다.
LIGHT = re.compile(r"突合|突き合わせ|照合|一覧|洗い出|棚卸|調べ|調査|確かめ|確認|点検|検索|"
r"探し|数え|集計|検査|テスト|check|list|search|find|grep|count|scan|test|inspect",
re.IGNORECASE)
...
판별에 사용하는 것은 Agent 도구의 짧은 설명문(description)만 사용했습니다💡
요청문의 본문에는 절차나 주의사항 안에 '확인해 주세요'가 거의 반드시 들어가기 때문에, 본문으로 판별하면 모든 것에 알리게 됩니다.
제 환경에서는 역할별로 일괄적으로 임무를 맡기는 서브 에이전트를 '책임자'라고 부르며 시작합니다.
이것은 Claude에게 남겨두는 작업이므로 알리지 않습니다. 대신, 요청문에 codex-sol로 보낼 지시가 작성되어 있지 않으면, 그것을 상위(parent)에게 알립니다.
앞 절의 sekininsha 정의로 시작했을 때는, 정의 본문에 보낼 지시가 있기 때문에, 요청문을 보지 않고 조용히 넘어갑니다.
막지 않는 이유
PreToolUse의 hook은 permissionDecision을 반환하면 시작을 거부할 수 있습니다.
그럼에도 불구하고, 굳이 반환하지 않았습니다. 반환하지 않으면, 시작은 일반적인 권한 흐름대로 그대로 진행됩니다.
막지 않는 이유는, 판별이 설명문의 단어에만 국한되어 있어 오판이 반드시 발생하기 때문입니다.
'테스트의 관점을 설계해 줘'처럼, 단어는 조사 같아도 내용은 설계라는 요청도 있습니다.
막는 hook으로 하면, 오판할 때마다 작업이 멈춥니다.
또 하나, 알리는 타이밍에도 주의가 있습니다.
공식 문서에서는 PreToolUse의 additionalContext는 '도구 결과 옆'에 삽입된다고 쓰여 있습니다. 즉 상위(parent)가 이 알림을 읽는 건, 시작이 끝나서 결과가 돌아왔을 때입니다.
그 시작 자체에는 맞출 수 없기 때문에, 알림 문구에는 '추후 전하거나, 다음 시작부터 요청문에 추가해 주세요'라고 적었습니다.
테스트는 실제 시작 설명문에서 만든다
판별 테스트는 대화 기록에 있던 실제 시작 설명문에서 만들었습니다. 지금은 20건입니다.
| 분류 | 건수 | 예 (일반화함) |
|---|---|---|
| 알리기 | 9 | '장부의 status 일람을 작성', 'home의 설정을 점검' 등, Explore 시작 |
| ... | ||
| 알리기만 하는 hook이므로, 예외가 발생해도 시작을 방해하지 않도록 마지막에는 반드시 종료 코드 0으로 빠지게 했습니다. |
4. 건수를 세고, 시험을 감사한다
위임했는지 센다
위임이 늘었는지를 확인하려면, 건수를 셀 수 있어야 합니다.
흔적은 두 곳에만 남습니다.
- Claude의 대화 기록 (
~/.claude/projects/의 JSONL) 중, Bash의 tool_use로codex-sol read|write를 호출한 줄 - 래퍼가 작성하는runs.tsv
대화 기록을 'codex-sol'로 단순 검색하면, CLAUDE.md에 주입된 분량까지 세어버리게 됩니다😅
그래서, tool_use의 줄만 볼 스크립트를 작성했습니다.
부모(親)와 서브 에이전트의 대화 기록을 모두 읽고, Agent의 실행 여부, codex-sol 호출 내역, runs.tsv의 행, 그리고 서브 에이전트가 실제로 작동한 모델들을 나열합니다. 단순히 읽기만 할 뿐, 아무것도 쓰지는 않습니다.
테스트를 2회 감사한 결과, 문제점 6가지 발견
시스템을 구현한 다음 날 아침(10월 6일), 두 가지 테스트를 실행하여 대화 기록을 감사했습니다. 감사는 총 2회 진행되었습니다.
1차에서 발견된 문제는 즉시 수정했고, 2차에서 발견된 문제도 같은 날 안에 고쳤습니다.
테스트 A는 부모 Claude가 3가지 리포지토리(repo)의 설정 변경을 codex-sol write로 3개 병렬 실행한 테스트입니다. 부모는 돌아온 차이점(diff)을 직접 읽고 확인했습니다.
테스트 B는 일체를 맡긴 서브 에이전트가, 설정 파일에 비밀 값이 섞여 있는지 검색하는 작업을 codex-sol read로 요청한 테스트입니다. 서브 에이전트는 결과를 받은 후, 설정의 원본과 실제로 배치된 버전 둘 다를 스스로 다시 검색했습니다.
판정은, 테스트 B는 합격, 테스트 A는 조건부 합격입니다.
1차 감사에서는 두 경우 모두 합격으로 판단했지만, 2차 감사에서 테스트 A를 수정하게 되었습니다.
이유는 부모의 첫 보고에 있었습니다.
표는 19행인데, 제목에는 '20개'라고 적었거든요…
차이점을 확인하는 방법은 정확했지만, 보고서의 숫자를 확인하지 못한 것입니다.
1차 감사에서 발견된 문제점은 다음 3가지입니다.
| 발견된 내용 | 수정 방법 |
|---|---|
| 부모가 Codex 답변의 마지막 4행만 읽고 미완료 사항을 놓칠 뻔함 | Wrapper가 마지막에 [codex-sol] 종료 코드 0・미완료 사항 없음 형태로 모아서 출력하고, runs.tsv의 7번째 열에도 남기도록 수정 |
| ... |
첫 번째는 종료 코드가 0이면 '완료'라고 오해하기 쉬운 실수였습니다⚠️
종료 코드만 보고 안심하는 경향이 생기기 쉽죠…
지금은 미완료 사항의 행을 '없음・있음・불명' 세 가지로 나누어 마지막에 출력하므로, 끝 부분만 읽어도 놓치지 않습니다💪
2차 감사에서는 추가로 3가지가 발견되었습니다👇
| 발견된 내용 | 수정 방법 |
|---|---|
| 보고 건수를 표의 행 수와 대조하지 않고 작성함 (테스트 A의 '20개') | 전역(global) CLAUDE.md에 |
스스로 요청한 변경인데, 라는 느낌을 받았습니다.
다만, 위임(委譲) 메커니즘 자체는 애초에 'Claude가 자신의 작업 방식을 바꾼다'는 것입니다.
그 변경이 사람의 손을 거치지 않고 들어오는 것이 더 무서우므로, 이번에는 제대로 작동했다고 받아들이고 있습니다💡
두 번째는 멈추지 않았다
하지만, 두 번째 감사 이후 수정에서는 결과가 달랐습니다🧐
같은 자동 허용 모드(automatic approval mode) 세션에서, hook의 書き換え(書き換え), 에이전트 정의 추가, 전역 CLAUDE.md의 書き換え를 Claude가 수행했지만, 어느 것도 멈추지 않았습니다.
건수 스크립트 수정은 이번에는 제가 터미널에서 수정한 것이 아니라, Claude가 codex-sol write에 맡겼습니다.
같은 종류의 변경이라도 판정은 매번 같지 않습니다.
판단하는 것은 모델이기 때문에, 변경의 작성 방식이나 전후 문맥에 따라 결과가 달라질 수 있다고 생각합니다.
자동 허용 모드를 '자가 수정(自己改変)을 반드시 멈춰주는 안전장치'로 믿는 것은 위험하다는 것을 알았습니다⚠️
hook이나 정의, CLAUDE.md의 변경은, 멈추지 않았더라도, 사람이 내용을 읽어본 후에 사용하는 것이 더 안전합니다.
보안 측면에서 진행하고 있는 것
지금까지의 이야기에서, 보안과 관련된 부분만 뽑아보겠습니다.
- 샌드박스는
read-only와workspace-write만을 사용하며, `approval_policy=
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기