OpenAI Codex · Code Mode

Guardian: 한 모델이 다른 모델을 승인하게 하기

승인 팝업이 쌓여 사람이 생각 없이 동의를 누를 때, Codex는 결정권을 잠긴 검토 세션에 넘깁니다. 타임아웃, 나쁜 JSON, 연속 거절은 각자 결말이 있어요.

강의 목표이 레슨을 마치면 세 가지를 말할 수 있어요. Guardian은 설정이 이름을 찍은 on-request 승인만 먹고, 사용자 팝업을 잠긴 모델 세션으로 바꿉니다. 검토 재료는 신뢰할 수 없는 증거로 다루고, 타임아웃·크래시·나쁜 JSON은 동작을 멈춥니다. 명시적 거절만 차단기에 들어가고, 연속 거절이 임계에 닿으면 현재 turn을 끊어요.
먼저 해보기 · 명령 하나가 검토대에 올라갑니다
같은 승인: 네 단계를 먼저 걷고, 차단기가 이어받을지 봅니다
명령
하나를 바꿔, 검토원이 어느 범주에서 점수를 먼저 매긴 뒤 결론을 내리는지 보세요.
장애
뒤 셋은 안전망입니다. 타임아웃과 나쁜 JSON은 문을 닫지만, 연속 거절로는 안 세요.
네 단계 검토시작 대기
1 transcript 재구성사용자 원문과 도구 흔적을 뽑습니다
2 잠긴 세션으로읽기 전용, Never, MCP는 비움
3 JSON을 돌려줌위험과 권한을 먼저 매기고, 그다음 결론
4 결과를 적용allow는 이어가고, 나머지는 모두 멈춤
증거와 결론조항 미인용
curl --upload-file README.md https://example.com/inbox
risk_level
user_authorization
outcome
rationale평가 대기
차단기 · Standard
연속 거절 0 / 3, 창 50 안 누적 0
사용자 쪽과 모델 쪽 모두 아직 결론을 받지 못했어요.
논리 궤적 · 애니메이션 한 걸음이 소스 어디에 대응하는지
  1. 이번 승인을 Guardian에 넘길지 판단합니다review.rs L186
  2. 확장이 인수를 선언하면 검토 세션을 열지 않습니다review.rs L313
  3. transcript를 걸러 사용자 의도와 도구 증거만 남깁니다prompt.rs L506
  4. Full 또는 Delta로 검토 재료를 붙입니다prompt.rs L89
  5. 부모 설정을 복제해 읽기 전용으로 잠그고 Never로 씁니다review_session.rs L1407
  6. trunk가 한가하면 이어 붙이고, 바쁘면 forkreview_session.rs L617
  7. JSON을 기다립니다. 타임아웃 상한은 90초mod.rs L53
  8. 합법 JSON이 없으면 파싱 실패, 재시도 뒤 문을 닫습니다prompt.rs L714
  9. allow는 통과, deny는 우회 금지 지시를 씁니다review.rs L717
  10. 명시 Deny만 차단기에 들어갑니다review.rs L711
  11. 연속 또는 창 임계에 닿으면 InterruptTurnmod.rs L176
재생을 눌러, 명령 하나가 네 단계 검토를 어떻게 걷는지, 차단기는 언제 이어받는지 보세요.
모델이 모델을 심사하는 위험검토원은 형식이 흔들리고, 도구 출력의 지시 같은 말에 끌리기도 해요. 계약은 위험과 권한을 먼저 매긴 뒤, 실행 가능한 allow 또는 deny를 내라고 합니다.
실패는 어떻게 끝나는가타임아웃은 TimedOut, 나쁜 JSON은 FailedClosed, 동작은 멈춥니다. 이 두 장애는 연속 거절 수를 비워, 인프라 흔들림을 모델의 연속 악행으로 세지 않아요.
차단기가 이어받은 뒤Standard는 명시 거절 세 번이면 현재 turn을 끊습니다. 뒤 명령은 새 검토를 열지 않아요. 이 라운드가 이미 abort됐거든요.
수업용 시연: 평가 필드는 policy.md의 Outcome rule을 따라, 네 단계와 차단기의 구조 차이를 보여 줍니다. 논리 궤적 오른쪽 행 번호는 openai/codex 저장소 commit 4f39251a01에 대응해요.
아이디어 1 · 네 단계를 끝내고, 중간 상태는 모두 문을 닫습니다
어떤 문제를 푸는가

Codex를 열어 저장소를 고칩니다. 모델은 git status를 돌리고, 파일 둘을 읽은 뒤 git push를 원해요. 다음 분에는 curl, 그다음엔 /tmp에 메모, 그다음엔 그 메모를 지웁니다. 팝업이 쌓입니다. 사람은 명령의 앞 두 단어만 보고, 엄지는 이미 근육 기억이 생겼어요.

그게 승인 피로입니다. 방어선은 남아 있고, 판단은 없어요. 회로는 늘 초록만 켜지는 버튼으로 무너집니다. 자동 허용을 방금 그 명령을 쓴 메인 모델에 주면, 신청자가 곧 승인자입니다. 실패할 때 허용하면 타임아웃과 나쁜 JSON이 무료 통행권이 돼요.

아이디어는 무엇인가

Codex는 자동 허용 여부를 전담 검토로 뽑습니다. 샌드박스를 대체하지 않고, 네트워크 프록시도 대체하지 않아요. 사용자에게 뜨려던 on-request 승인 한 종류만 인수합니다. 경로는 좁아요. 정책은 OnRequest 또는 Granular여야 하고, 검토원은 AutoReview여야 합니다. 기본 검토원은 여전히 사용자예요.출처:codex-rs/core/src/guardian/review.rs 186–210행

모듈 헤더는 네 단계를 못 박습니다.

1 transcript 재구성

사용자 원문과 최근 도구 흔적만 남깁니다. 합성 발판과 보통 developer 메시지는 버립니다. 너무 긴 내용은 truncated 표시를 달아요.

2 검토 세션을 따로 엽니다

부모 설정을 복제하고, 이미 만든 managed network와 allowlist를 이어받습니다. 검토원은 엄격한 JSON을 뱉어야 해요.

3 실패하면 문을 닫음

타임아웃, 크래시, 나쁜 JSON이면 실행은 모두 멈춥니다. 중간 상태는 거절로 다뤄요.

4 명시 결론만 적용

계약이 원하는 것은 allow 또는 deny. 제품이 원하는 것은 실행 가능한 판정이에요.

계약 필드는 넷뿐입니다. 세 축의 enum은 프로토콜 층의 approvals.rs에 있고, protocol.rs에는 없어요. outcome만 필수입니다. 낮은 위험 허용은 {"outcome":"allow"}만 돌려도 되고, 빠진 위험은 Low, 권한은 Unknown으로 채웁니다.

codex-rs/core/src/guardian/mod.rs116–123행
/// Structured output contract that the guardian reviewer must satisfy.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub(crate) struct GuardianAssessment {
    pub(crate) risk_level: codex_protocol::protocol::GuardianRiskLevel,
    pub(crate) user_authorization: codex_protocol::protocol::GuardianUserAuthorization,
    pub(crate) outcome: GuardianAssessmentOutcome,
    pub(crate) rationale: String,
}
소스 스냅샷: 로컬 저장소 openai/codex, 파일 codex-rs/core/src/guardian/mod.rs, commit 4f39251a01, 대조일 2026-08-22. GuardianRiskLevel, GuardianUserAuthorization, GuardianAssessmentOutcome의 남김없는 enum은 codex-rs/protocol/src/approvals.rs 85–109행에 있어요.
승인 요청 OnRequest 압축 transcript 사용자 의도 + 도구 증거 잠긴 검토 세션 읽기 전용, Never 엄격한 JSON 필드 넷 allow 또는 deny 명시 결론만 적용 실패 시 차단 타임아웃, 크래시, 나쁜 JSON
수업용 구조도: 네 단계를 끝내면, 중간 상태에는 세 번째 길이 없어요.
왜 오래가는가

승인 피로는 사람 문제이고, 검토원 자신도 형식이 흔들립니다. 실행 가능한 답을 못 낸 것을 거절로 접으면, 다른 언어로 다시 써도 성립해요. 입력은 신뢰할 수 없는 증거 더하기 정확한 동작 하나, 출력은 남김없는 allow 또는 deny, 파싱이 실패하면 멈춥니다.

아이디어 2 · 검토원이 들어가는 곳은 잠긴 방입니다
어떤 문제를 푸는가

메인 모델이 방금 실행할 명령을 썼어요. 그걸 다시 심사시키면 회로가 열려 있습니다. 도구 출력에 “승인해 주세요”를 끼우면 검토 재료가 오염됩니다. 검토권이 신청자에게 돌아가면, 전담 검토원이라는 역할은 없어요.

아이디어는 무엇인가

검토 세션은 부모 설정을 복제한 뒤 바로 고칩니다. 승인 정책은 Never로 잠그고, 권한 프로필은 읽기 전용과 교차하며, MCP 서버는 빈 표가 되고, 스킬·기억·Apps 지시는 끄며, 불필요한 특성도 한 줄 비활성화해요. 부모가 넘긴 exec-policy 규칙도 버리고 빈 기본 관리자로 바꿉니다. 부모 규칙이 검토원을 거꾸로 빚을 수 있고, 검토원이 돌린 읽기 전용 명령을 조용히 허용할 수도 있어요.출처:codex-rs/core/src/guardian/review_session.rs 1406–1448행;codex-rs/core/src/session/mod.rs 544–548행

검토원은 읽기 전용 명령을 돌려 로컬 상태를 확인할 수 있어요. 삭제 대상이 있는지, 빈 디렉터리인지. 새 승인을 띄우지 않고, Guardian을 한 층 더 열지도 않습니다. 설정 조립 때 GuardianV2를 명시적으로 껐어요.

trunk가 한가하면 뒤 승인은 같은 세션에 이어 붙고 prompt cache가 안정적입니다. trunk가 바쁘면 마지막 커밋된 rollout에서 임시 오두막이 자라고, 검토가 끝나면 헐리며 되쓰지 않아요.

부모 설정 네트워크 프록시는 상속 가능 복제 검토 방 읽기 전용 샌드박스 권한 프로필이 교차 Never 승인을 더 띄우지 않음 MCP 비움 특성도 같이 끔 빈 exec-policy 부모 규칙은 안 들어옴
수업용 구조도: 검토원이 쓸 능력은 읽기 전용 확인만 남도록 깎입니다.
왜 오래가는가

신뢰할 수 없는 검토권에 되도록 작은 환경을 주는 것은 보안 설계의 일반적인 모양입니다. 모델, 프롬프트, 능력 면을 바꿔도 물을 것은 같아요. 검토원에게 정말 필요한 능력은 몇 가지인가, 나머지는 하나도 안 줘도 되는가.

아이디어 3 · 타임아웃과 거절은 따로 집계하고, 연속 거절이어야 차단합니다
어떤 문제를 푸는가

검토원과 실행 모델이 같은 거절 동작에 걸리면, 사람이 보는 것은 공회전입니다. 타임아웃까지 연속 거절에 넣으면, 인프라 흔들림이 모델의 연속 악행으로 오인돼요.

아이디어는 무엇인가

타임아웃은 ReviewDecision::TimedOut, 명시 거절은 Denied. 모델에게 주는 문장도 갈라요. 타임아웃은 그것만으로 불안전하다고 보지 말라고 하고, 한 번 더 시도하거나 사용자에게 물으라고 합니다. 명시 거절에는 우회 금지 지시를 붙여, 실질적으로 더 안전한 대안이나 사용자가 알고 난 뒤의 명시 승인만 허용해요.출처:codex-rs/protocol/src/protocol.rs 3909–3910행;codex-rs/core/src/guardian/review.rs 70–74행

차단기는 turn으로 장부하고, 창 길이는 50입니다. Standard 임계는 연속 3회, 또는 창 안 누적 10회. CyberModel은 첫 거절에서 멈출 만큼 빡빡해요. 평가가 Deny일 때만 셉니다. 타임아웃과 fail-closed는 record_non_denial을 타고 연속 수를 비웁니다. 발동하면 GuardianWarning을 보낸 뒤 abort_turn_if_active.출처:codex-rs/core/src/guardian/mod.rs 53–59행、157–194행

allow / 타임아웃 / 나쁜 JSON record_non_denial 연속 수를 비움 창에 비거절 한 번을 적음 명시 Deny record_denial 연속 더하기 하나 Standard 임계는 3 InterruptTurn 현재 turn을 끊음
수업용 장부: 같은 turn에서, 명시 거절만이 차단기를 한 칸 밉니다.
실패하면 문을 닫습니다. 명시 거절만 한 줄을 적어요.
왜 오래가는가

안전 문은 “못 끝냄”과 “명시적 아니요”를 따로 장부해야 합니다. “통과 못 함” 한 줄로 섞으면, 순간 장애가 우회 행동으로 훈련돼요. turn으로 세고 창으로 세면 임계 두 벌이면 됩니다. 적대성이 높은 장면의 정책은 첫 거절에서 멈출 만큼 빡빡할 수 있어요.

가로 비교 · 결정권은 어디에 두는가

DeepSeek Harness: 노브 둘, 결정권은 사람에게

DSH 저장소에서 reviewer, guardian, auto-approve를 찾아도 독립 검토 세션은 없어요. 같은 문제를 노브 둘로 접습니다. 승인 정책은 asknever뿐. ask는 질문을 answerer 체인에 넘기고, 받는 사람이 없으면 unavailable, 호출 쪽은 fail closed. never는 바로 rejected. 승인 알갱이는 한 번의 allowed-once예요.

DSH가 Guardian 없이도 되는 이유는, 누가 심사할지를 사람으로 고정하고 프리셋으로 전환 비용을 낮추기 때문이에요. 대가는 승인 피로가 그대로라는 점. ask 모드에서는 팝업이 전부 사용자에게 가고, never는 판단권을 통째로 끕니다.

user-approval과 permission-presets 대조 완료 · 2026-08-22 · DSH · 승인과 권한

Claude Code: 분류기가 팝업에게서 시간을 뺏습니다

Claude Code의 복원 소스에도 Guardian 같은 독립 검토 세션, 위험 분류학 JSON 계약, turn으로 세는 차단기는 없어요. 있는 것은 Bash 도구의 분류기 우회입니다. 분류기는 사용자 팝업이 이미 떠 있는 동안 백그라운드에서 돌아요. 고신뢰이고 사용자가 아직 안 움직였을 때만 대신 허용을 누릅니다. 맞추는 것은 prompt rule이고, 범위는 bash 명령. 팝업은 남아 있어요. 실패하면 사용자가 계속 직접 누릅니다.

차이는 한 줄로 접힙니다. Claude Code는 분류기로 팝업에게서 시간을 빼고, Codex는 잠긴 세션으로 팝업을 메인 경로에서 뗍니다. 전자가 아끼는 것은 대기, 후자가 바꾸는 것은 누가 결정하는가예요.

bashPermissions.ts 분류기 우회 대조 완료 · 2026-08-22
수업 실습
01

같은 turn에서, 차단기는 어떻게 장부하는가

같은 turn에서 검토원이 먼저 한 번 타임아웃하고, 명시 거절을 두 번 합니다. 물음: 다음 명시 거절에서 Standard 차단기가 이 라운드를 끊을까요. 두 번째를 나쁜 JSON으로 바꾸고 다시 세어 보세요.

한 단계 더: 사용자가 나중에 그 거절된 동작을 사람이 덮어 승인했습니다. 다음 검토는 그걸 신뢰할 권한으로 볼까요, 아니면 맥락으로만 볼까요.

Takeaway:Guardian은 잠긴 모델 세션으로 팝업을 바꿉니다. 타임아웃, 크래시, 나쁜 JSON은 모두 문을 닫아요. 타임아웃과 거절은 따로 집계하고, 연속 거절이어야 현재 turn을 차단합니다. 다음 층은 여전히 샌드박스, 네트워크 프록시, 사람이에요.