지식위키

하네스 설계 원칙 — 7개 하네스가 수렴한 10개 법칙

하네스 설계 원칙 — 7개 하네스가 수렴한 10개 법칙

한 줄 요약

서로 베끼지 않은 7개의 AI 하네스가 같은 10개 설계에 독립적으로 도달했다 — 이건 취향이 아니라 “LLM이 못 미덥고·무상태이고·자원이 유한하다”는 제약이 강제한 법칙이다. 왜 배우나: 이 10개를 알면, 내 하네스를 만들 때 “어디를 코드로 막고 어디를 산문으로 두며 어디서 비용을 아낄지”를 처음부터 옳게 설계할 수 있다.


그림

1_하네스_설계원칙-diagram.svg

10개 원칙은 결국 두 갈래로 묶인다 — “모델을 어떻게 못 믿을 것인가”(불신 계열)와 “유한 자원을 어떻게 아낄 것인가”(절약 계열), 그리고 둘을 잇는 접착제(계약·루프).

flowchart TD
    R["LLM의 3가지 한계<br/>자기보고 · 무상태 · 유한자원"] --> A["불신 계열<br/>모델을 어떻게 못 믿을까"]
    R --> B["절약 계열<br/>유한 자원을 어떻게 아낄까"]
    R --> C["접착제<br/>표준 계약 · 안전한 루프"]

    A --> A1["1 코드가 완료를 판정"]
    A --> A2["2 검증자와 작업자 분리"]
    A --> A4["4 절대규칙은 런타임 강제"]
    A --> A10["10 권한은 deny-first 다층"]

    B --> B3["3 컨텍스트는 예산"]
    B --> B7["7 위임은 메인 컨텍스트 보호"]
    B --> B8["8 안전 깊이를 위험에 비례"]
    B --> B9["9 상태는 세션 너머 영속"]

    C --> C5["5 stdin/stdout JSON 단방향 계약"]
    C --> C6["6 종료 게이트 + 무한루프 가드"]

[!info] 7개 하네스가 누구인가 Claude Code · Codex · OMC · gajae-code · ouroboros · fable-ish · 내 패턴. 각 원칙은 이들의 횡단노트 “공통 패턴”에서 도출했고, 모든 원칙에 그 법칙이 깨지는 반례(언제는 안 따라도 되는가)를 함께 박았다.


쉽게 풀기

이 노트를 처음 읽는 사람을 위해, 10개 원칙을 “AI 직원을 두는 사장님” 비유로 풀어본다.

당신이 AI 직원(LLM)을 고용했다. 똑똑하지만 세 가지 버릇이 있다.

  1. 말로는 다 했다고 한다 — 실제로 했는지는 모른다. (자기보고)
  2. 어제 일을 기억 못 한다 — 매일 아침 백지에서 출근한다. (무상태)
  3. 책상이 좁다 — 한 번에 볼 수 있는 서류 양이 정해져 있다. (유한 컨텍스트)

이 세 버릇을 다루는 게 하네스 설계의 전부다.

  • 불신 계열 — “다 했어요”를 믿지 말고 결과물을 자동 검사기로 확인한다(원칙 1). 일한 사람이 스스로 채점하면 후하니 채점자를 따로 둔다(원칙 2). “절대 하지 마”라고 메모에 써봤자 안 지키니 문을 아예 잠가둔다(원칙 4). 위험한 권한은 “되는 것만 허용”이 아니라 “안 되는 걸 먼저 막고 여러 겹으로” 잠근다(원칙 10).
  • 절약 계열 — 좁은 책상에 서류를 다 못 올리니 필요한 것만, 늦게, 요약본으로 올린다(원칙 3). 부하 직원에게 일을 시킬 땐 산더미 결과물이 아니라 요약 보고서만 받는다(원칙 7). 모든 일에 풀 검수를 하면 비싸니 위험한 일에만 빡빡하게 군다(원칙 8). 매일 기억을 잃으니 수첩(디스크)에 적어두고 다음 날 다시 읽힌다(원칙 9).
  • 접착제 — 직원과 부서가 소통하는 **표준 양식(JSON 한 장)**을 정하고(원칙 5), 일을 끝내려 할 때 “한 번 더 점검”하되 무한 반복에 빠지지 않게 횟수 제한을 건다(원칙 6).

핵심은 이것이다 — 부탁(산문)으로 될 일과, 강제(코드)로만 될 일을 구분하는 것. 되돌릴 수 없는 행동(배포·삭제·커밋)은 코드로 막고, 나머지는 가이드로 둔다.


핵심 정리

10개 원칙을 “원칙 / 강제하는 제약 / 한 줄 반례”로 슬림하게 정리한다.

원칙강제하는 제약대표 반례
1. 코드가 완료를 판정자기보고 비용=0검증 불가 작업은 “변경 없음=정상”
2. 검증자≠작업자자기채점 확증편향오타 한 줄까지 별도 리뷰면 너무 느림
3. 컨텍스트는 예산유한 윈도우·캐시항상 필요한 Seed는 통째 싣기
4. 절대규칙은 런타임 강제산문은 건너뛰어짐identity 규칙은 경로차단 불가
5. JSON 단방향 + fail-open 기본언어중립·프로세스격리보안 판정은 fail-closed
6. 종료 게이트 + 루프 가드무한 핑퐁 실패모드대화형은 사용자 중단 허용
7. 위임=컨텍스트 보호메인 창 오염 방지raw는 artifact로 보존(재조회용)
8. 안전을 위험에 비례균일 검수=비용폭발비가역 행동은 항상 최대 게이트
9. 상태는 세션 너머 영속모델은 무상태MD는 대규모 쿼리에 약함
10. 권한 deny-first 다층안전실패 비대칭 비용겹이 많을수록 유지보수 무거움

[!note] 두 갈래로 읽는 법

  • 불신 계열(모델을 못 믿는다): 원칙 1·2·4·10 — 좋은 하네스 설계의 절반.
  • 절약 계열(자원을 아낀다): 원칙 3·7·8·9 — 나머지 절반.
  • 접착제(계약·루프): 원칙 5·6 — 둘을 잇는다.

[!tip] 모든 반례를 관통하는 한 가지 “이 원칙을 언제나 적용하라”는 없다. 반례는 거의 항상 같은 모양이다 — 비가역·고위험 경계(commit/배포/삭제/외부 API)에는 최대 강도, 사소·가역 작업에는 가볍게. 균일하게 빡빡하면 사용자가 하네스를 꺼버린다.


실제 예시

각 원칙이 “누가 실제로 그렇게 짰는가”의 근거다. 원칙 → 왜 → 근거 프레임워크(파일경로) → 반례 순으로 본문을 보존한다.

원칙 1. 모델의 자기보고를 증거로 받지 마라 — 완료는 코드가 판정한다

원칙. “다 했습니다 / 테스트 통과했습니다”라는 모델의 말을 완료 신호로 쓰지 않는다. 완료 판정을 LLM 바깥의 결정적 레이어(Stop 훅, exit code, sha256 영수증, 임계값)로 빼내고, 관찰 가능한 증거(테스트 exit_code 0, diff, 체크섬)만 신뢰한다. 구체적으로: PostToolUse가 변경파일·검증결과를 원장에 누적 → Stop 훅이 “변경됐는데 검증 증거 없음”이면 종료를 decision:block으로 거부.

왜. LLM은 통계적으로 “완료했다”고 말하는 비용이 0이라 자기보고를 남발한다(80_verification 제1원리, 학습인사이트 메타교훈 2). 지시문(soft)으로 “검증해”라고 하면 안 지키는 순간이 반드시 온다. 결정적 게이트(hard)만이 “같은 입력→같은 판정”이라 가드레일로 신뢰 가능하다.

근거 프레임워크. fable-ish(should_block_stop이 ledger의 verification_results[].success가 하나라도 있어야 verified로 인정) · 내 패턴(gate.mjs requireMilestone 미통과 시 exit 2 물리 거부, admit.mjs frontmatter 전수검사) · gajae-code(sha256 영수증으로 완료를 fail-closed 증명) · Codex(ReviewTask + Guardian LLM 심판). 80_verification 공통패턴 1·2.

반례. 검증 불가능한 작업(디자인 토론, 탐색적 리서치, 단순 분석/설정 확인)에서는 “변경 없음=정상 완료”가 합법인데 changed_count>0을 강제하면 정당한 종료가 막힌다. 그래서 fable-ish는 quick 모드를 게이트에서 면제하고 MAX_STOP_BLOCKS=2로 두 번만 막고 양보한다. OMC verify-deliverables.mjs는 정반대 반례 — 가장 정교한 검증 문화를 갖췄지만 무한루프 버그(#3209) 회피를 위해 마지막 게이트를 ADVISORY 비차단으로 후퇴시켜 “문화는 최강, 강제력엔 구멍”이 됐다.

원칙 2. 검증자와 작업자를 분리하라 — 자기 채점을 구조로 금지한다

원칙. 코드를 짠 컨텍스트가 그 코드를 스스로 승인하지 못하게 한다(“Never self-approve in the same active context”). 검증/리뷰를 독립된 에이전트·세션·프로세스로 떼어내고, 검증자에게는 read-only 권한만 준다.

왜. 출제자와 채점자가 같으면 확증편향으로 false approval(거짓 합격)이 난다. 이 비용은 비대칭적으로 크다 — OMC는 “false approval은 정답 합격보다 10~100배 비싸다”고 모델링한다. 같은 컨텍스트는 자기 출력의 가정을 의심하지 못하므로 적대적 검토 자체가 성립하지 않는다.

근거 프레임워크. OMC(critic 다단계 적대 리뷰 Pre-mortem→Multi-perspective→Self-Audit→ADVERSARIAL, critic은 disallowedTools로 read-only 강제) · 내 패턴(writer≠reviewer를 OMC뿐 아니라 작업 리포 human-token-workflow/AGENTS.md에 한국어로 재강제) · Codex(검증을 별도 codex 세션/Guardian에 외주) · ouroboros(구현=UNRESTRICTED vs 평가=READ_ONLY 권한 분리). 80_verification·60_orchestration 공통패턴 4.

반례. 검증 에이전트를 한 번 더 돌리면 비용·지연이 2배다. 모든 작업(오타 수정 한 줄)에까지 별도 reviewer 패스를 강제하면 느리고 비싸 사용자가 하네스를 꺼버린다. 분리 검증은 비가역·고위험 경계(commit/배포/대형 변경)에 집중하고, 사소한 변경은 결정적 게이트(원칙 1)로만 통과시키는 게 현실적 균형.

원칙 3. 컨텍스트는 무한 자원이 아니라 예산이다 — 얇게·늦게·포인터로

원칙. 거대한 전체를 미리 안 넣고 “모델에게 보낼 직전 최소한으로” 조립한다. 세 가지 손잡이: ① 지연 로드(평소엔 description/디스크립터만 상주, 매칭 시 본문 펼침) ② 포인터 회피 인라인(본문은 디스크, 컨텍스트엔 경로+요약) ③ 예산 상한(글자수·줄수·세션당 개수 캡). 도구가 수십~수백 개가 되면 BM25 검색 + deferred로 온디맨드 노출.

왜. 무상태 LLM + 유한 컨텍스트 윈도우라는 동일 제약 때문에 토큰 경제학이 “포인터+지연로드+예산”을 강제한다. 많이 넣을수록 비싸고, 핵심이 노이즈에 묻히고, 프롬프트 캐시가 깨진다(context rot/pollution).

근거 프레임워크. Claude Code(스킬 3단계 progressive disclosure, MEMORY.md 200줄/25KB 컷, ToolSearch+deferred) · Codex(스킬 예산 3단 강등, MCP 도구 100개 이상이면 자동 deferred, 스키마 sanitize→prune→compact) · gajae-code(loadMode: essential/discoverable, withDeadline(5s), MCP 이름순 정렬로 캐시 breakpoint 보존) · OMC/내 패턴(얇은 디스패처 description:"" + 두꺼운 SKILL.md 지연로드). 20_context-assembly 공통패턴 2·4, 40_skills-commands 공통패턴 2, 50_tools-mcp 공통패턴 4.

반례. 컨텍스트를 아끼면 모델이 “전체 그림”을 못 봐서 국소적 결정만 한다(학습인사이트 2-2). 또 모든 게 지연로드는 아니다 — 부팅 성격의 능력 등록(내 패턴의 plugin capabilities·시스템 리마인더), ouroboros의 불변 Seed처럼 작고 항상 필요한 것은 세션 앞단에 통째 싣는 게 맞다. “큰 본문은 정적 prefix/파일, 동적 선택만 descriptor/포인터”가 정확한 경계(Codex 교차검증).

원칙 4. 절대 규칙은 산문이 아니라 런타임으로 강제하라

원칙. 정말 막아야 하는 것은 CLAUDE.md/SKILL.md 산문에 “절대 X 하지 마”라고 쓰지 말고, 훅·exit code·OS 샌드박스·권한규칙으로 코드 강제한다. 산문은 판단 형성용 가이드로만 남긴다. 강제력을 “막는 것(hard, 런타임)“과 “부탁하는 것(soft, 프롬프트)“으로 명시 분리.

왜. 모델은 프롬프트 인젝션으로 속거나 그냥 산문 규칙을 건너뛴다. “런타임이 강제하지 않으면 규칙은 권고일 뿐”이다(학습인사이트 원칙 10). 그래서 7개 전부 OS 샌드박스/hook deny/throw/실행봉투로 진짜 차단을 모델 바깥에 둔다 — 모델이 속아도 OS/런타임이 막아야 한다는 공통 위협모델.

근거 프레임워크. Codex(Starlark 실행정책 + OS 샌드박스 3종 = 코드 강제, 트랜스크립트를 “untrusted evidence”로 라벨) · Claude Code(permissions.deny는 hook으로도 못 풀고, 보호경로 .git/.claude는 allow로도 못 뚫음) · 내 패턴/OMC(realpathSync 심링크 해소 후 레포밖/민감경로 거부) · fable-ish(역설적 근거 — 정규식 한계를 인정하고 “Rely on Claude Code permissions for hard enforcement”라고 하드 강제를 호스트에 명시 위임). 70_guardrails-sandbox 공통패턴 2, 30_hooks 공통패턴 3.

반례. 코드 강제는 진짜 잠그지만 만들기 어렵고 유연성이 없다. 또 settings.deny만으로는 못 막는 규칙이 있다 — writer≠reviewer 같은 workflow/identity 규칙은 경로·명령 차단으로 표현 불가라 ledger/Stop 훅 검증이 필요하다(70 교차검증). 핵심 규율(되돌릴 수 없는 행동)만 코드로, 나머지는 산문으로 두는 게 비용 효율적.

원칙 5. 호스트↔확장은 stdin/stdout JSON 단방향 계약으로 — 그리고 fail-open이 기본값

원칙. 확장(훅/플러그인)은 이벤트 페이로드를 stdin JSON으로 받고, 컨텍스트 주입은 hookSpecificOutput.additionalContext, 통제는 permissionDecision/decision:block을 stdout 한 줄로 뱉는다(message 같은 임의 필드는 무효). 그리고 보조 장치(로그·추천·주입) 훅은 fail-open(예외 시 {continue:true}/exit 0)으로 작업을 절대 막지 않는다. 반대로 보안·검증 판정은 fail-closed.

왜. 단방향 JSON 계약은 언어 중립성(Python/Node/셸 무관) + 프로세스 격리(훅 크래시가 호스트를 안 죽임) + 단위 테스트 용이성을 동시에 준다 — 그래서 Codex가 Rust로 다시 짜면서도 같은 형태를 복제했다. fail/안전 정책이 갈리는 이유: “도구 래퍼의 신뢰성”(작업이 막히면 생산성 0)과 “보안 결정의 안전성”(잘못 통과하면 품질 0)은 반대 방향으로 틀려야 옳다.

근거 프레임워크. OMC·fable-ish·내 패턴(동일 additionalContext 봉투, run.cjs/SystemExit(0) fail-open) · Codex(HookAdditionalContext를 developer 역할로 주입, Guardian은 타임아웃/파싱실패=거부 fail-closed) · gajae 쓰기(체크섬 검증 실패 시 throw) vs 읽기(lenient passthrough)의 의도적 비대칭. 30_hooks 공통패턴 2·5, 70_guardrails-sandbox 공통패턴 5, 100_state-memory 분기 4.

반례. “모든 훅 fail-open”은 위험한 과잉 일반화다 — 권한 차단/산출물 검증/모델 라우팅 강제를 fail-open하면 가드가 깨졌을 때 그대로 우회된다(30 교차검증). 또 디스크 쓰기를 전부 fail-closed로 묶는 것도 과하다: 코드 파일은 git으로 되돌릴 수 있으니 fail-closed 대상은 approval receipt·state ledger·commit/push/deploy·외부 API 호출처럼 “의미가 남는 비가역 쓰기”로 좁혀야 한다(80 교차검증).

원칙 6. 종료 직전에 게이트를 두되, 무한루프 가드를 짝으로 박아라

원칙. 모델이 끝내려는 순간(Stop)을 가로채 “한 바퀴 더 돌릴” 확장점을 둔다. 단 루프를 굴리는 능력과 멈추는 안전장치는 항상 한 쌍으로 온다 — N회 연속 block 상한, 재진입 가드(stop_hook_active), 반복 실패 격리(quarantine)를 반드시 함께 둔다.

왜. 자기참조 루프(검증 거부→재시도→다시 거부)의 1번 실패모드가 모델↔게이트 무한 핑퐁이다. 상한이 없으면 영원히 갇힌다. 그래서 “두 번 알리고 양보” 혹은 “N회 상한”이 보편 해법이 됐다.

근거 프레임워크. Claude Code(8연속 block이면 훅 무시·강제 종료) · fable-ish(MAX_STOP_BLOCKS=2 + stop_hook_active 재진입 차단) · OMC/내 패턴(랄프 루프 + hardMax·stale/context/auth/user-stop guard) · Codex(서킷브레이커 거부 3/10회) · ouroboros(max_generations + 진동/정체/반복질문 별도 종료) · 내 패턴(2회 실패→quarantine 에스컬레이션). 10_agent-loop 공통패턴 2, 90_self-improvement 공통패턴 2, 80_verification 공통패턴 3.

반례. Stop 게이트의 용도가 둘로 갈린다 — fable-ish는 soft(한 번 상기시키고 양보, 모델 자율 존중), OMC 랄프는 hard(끝낼 때까지 강제 재주입). 자율 모드에서는 “사람에게 묻지 마라”가 정책이지만(OMC self-improve “NEVER stop to ask”), 대화형 작업에서 같은 강제 루프를 쓰면 사용자가 멈추고 싶을 때 못 멈춘다. 그래서 무한루프 가드는 반드시 user abort·권한 대기·context-limit·auth 실패를 예외로 통과시켜야 한다.

원칙 7. 위임의 1차 목적은 일 나누기가 아니라 메인 컨텍스트를 지키는 것이다

원칙. 서브에이전트는 별도 컨텍스트 창/스레드/세션으로 격리하고, 부모에게는 raw가 아니라 요약/영수증만 반환한다(outputRef=agent://<id>+sha256, 필요하면 다시 읽기). 그리고 재귀 폭발 방지(depth 제한 또는 “NEVER spawn sub-agents” 프리앰블)와 동시성 상한을 1급 안전장치로 둔다.

왜. 멀티에이전트를 구현한 6개가 똑같이 쓰는 정당화 한 문장: 대량 출력(로그·테스트·fetch)이 서브 창에 갇히고 메인엔 결론만 올라온다. 격리해놓고 raw를 도로 부으면 격리가 무의미하므로 반환 경계에 압축/계약을 강제한다. subagent가 또 subagent를 무한히 낳으면 비용·혼돈이 폭발한다.

근거 프레임워크. Claude Code(named subagent는 CLAUDE.md+git만 상속, SubagentStop에 요약만) · gajae-code(buildTaskReceipt + BANNED_RAW_TASK_KEYS로 raw 표면 누출 차단, raw는 <id>.md artifact로 보존) · Codex(<subagent_notification>의 last_agent_message, depth/slot 제한) · ouroboros(AC별 세션, 자기 AC+형제경계만 받음). 60_orchestration 공통패턴 1·2·3.

반례. “요약만 반환(raw 금지)“은 보편 법칙이 아니다 — Claude 문서는 자세한 결과가 컨텍스트를 먹는다고 경고할 뿐 금지하지 않고, gajae는 raw를 artifact로 보존한다(60 교차검증). 단순히 “본문을 안 보낸다”만 가져오면 디버깅성과 신뢰성이 떨어지므로, 부모가 path+sha256으로 원문을 다시 조회·검증할 수 있어야 한다. 또 fable-ish는 멀티에이전트를 의도적으로 안 한 반례 — “위임 없이 훅 3개만으로 같은 규율(검증 전 완료 금지)“이 가능함을 보여준다.

원칙 8. 안전·검증의 깊이를 위험에 비례시켜라 — 균일하게 빡빡하게 굴지 마라

원칙. 모든 작업에 최대 안전을 적용하지 말고, 작업을 분류(quick/normal/deep/blocked, 또는 모델 티어 haiku/sonnet/opus)해 검증 깊이와 권한·비용을 차등한다. 게이트 거부 결과는 사람이 아니라 모델에게 텍스트로 피드백해 “에러”가 아닌 “재계획 신호”로 준다.

왜. 모든 작업에 풀 파이프라인을 돌리면 비용·지연이 폭발하고 사용자가 하네스를 꺼버린다. 안전은 위험이 있는 곳에만 집중해야 비용 대비 효과가 산다. 차단 사유를 모델에게 돌려주는 이유: 모델은 막힌 후에도 계속 돌아야 하므로 차단을 재계획 신호로 줘야 루프가 안 죽는다.

근거 프레임워크. fable-ish(위험비례 동적 탈출조건, 4모드 분류) · Claude Code(모델 티어별 비용/정밀도 분배, paths glob으로 해당 파일군에만 스킬 자동활성) · OMC/내 패턴(haiku=조회/sonnet=구현/opus=설계 frontmatter 명시, 모델 라우팅을 비용최적화 도구로) · ouroboros 3단계 비용 계단(mech $0 → semantic LLM → consensus). 70_guardrails-sandbox 공통패턴 4, 80_verification 분기점, 학습인사이트 원칙 6.

반례. 분류 자체가 휴리스틱이라 오분류 위험이 있다 — “위험한데 quick으로 분류”되면 그대로 통과한다. fable-ish의 샘플/예시 단어 하나로 sensitive 해제되는 회피 표면이 그 예. 비가역·고위험(commit/배포/rm -rf/외부 API)은 분류와 무관하게 항상 최대 게이트를 거는 fail-safe 하한선이 필요하다.

원칙 9. 상태는 세션을 넘어 살아남아야 한다 — 원자 쓰기·세션격리·수명분리로

원칙. “모델은 무상태”를 제1전제로 삼아 결정·함정·관계를 디스크(MD/JSONL/SQLite/이벤트스토어)에 영속화하고 다음 세션에 재주입한다. 영속화의 3대 위생수칙: ① 원자적 쓰기(tmp→rename, 로그는 append+flush) ② 세션 격리 키(sessions/<id>/ 또는 sha256(session_id|cwd)) ③ 수명 분리(항상 로드 priority / N일 prune / 영구). 진실원본(SSOT)과 파생캐시를 분리하고 파생물은 언제든 재생성 가능하게.

왜. 같은 실수를 반복하지 않으려면 지식이 복리로 쌓여야 한다(Karpathy식 누적). 멀티 세션 동시 실행이 전제라 파일이 반쯤 쓰이다 깨지면 안 되고(원자 쓰기), 세션끼리 안 섞여야 하며(격리 키), 전부 영구로 두면 컨텍스트가 폭발하므로(수명 분리) 싸게 주입할 얇은 인덱스와 무거운 본문을 가른다.

근거 프레임워크. Codex(rollout JSONL=SSOT + state SQLite=파생캐시, Memories 2단계 추출 + 비밀 redaction) · OMC/내 패턴(.omc/ 4계층, atomic write, project-memory critical:/note:가 compaction 견딤, PID-aware liveness로 죽은 소유자 상태 회수) · ouroboros(이벤트소싱 SSOT, 정정도 새 이벤트, replay로 lineage 재구성) · gajae(읽기 lenient / 쓰기 strict 비대칭 + 30분 fresh_until). 100_state-memory 공통패턴 1·2·3·4·7, 학습인사이트 원칙 7.

반례. 마크다운은 단순·이식 쉬움이지만 쿼리·정렬·대규모 누적에 약하다(Claude Code 약점) → 그래서 OMC는 wiki에 index.md를, Codex는 SQLite를 추가로 둔다. 반대로 이벤트소싱은 replay가 강력하지만 복잡·학습곡선·디버깅 부담이 크다(ouroboros) → 학습용·소규모엔 과하다. “감사·재현이 핵심이면 이벤트소싱, 아니면 마크다운”이 갈림선. 또 모델이 직접 쓰는 메모리(자동메모리/Memories)는 자율적이나 오염·과잉일반화 리스크가 있어 redaction과 diff review가 필요하다.

원칙 10. 위험한 권한은 deny-first로, 여러 겹으로 막아라

원칙. 권한 평가는 허용보다 금지를 먼저(deny→ask→allow 첫매칭, 또는 다매칭 시 가장 제한적 결정 max()). 한 줄짜리 정규식 한 겹을 믿지 말고 OS 샌드박스 + 실행정책 + 정적판정 + (선택적) LLM 검토를 독립적으로 겹쳐 쌓는다(defense in depth). 셸 메타문자(; & | $ ( ) \``)는 화이트리스트 검사 *전에* 즉시 탈락시키고, 민감경로(.git/.env/.ssh`/secrets)는 하드코딩 블랙리스트로 특별취급.

왜. 허용을 빠뜨리면 불편하지만 금지를 빠뜨리면 사고난다 — 안전 실패는 비대칭적으로 비싸므로 “막는 쪽으로 틀린다”. 정규식 한 겹은 우회된다(fable-ish 자체 측정: 16개 위험 패턴 중 4개만 잡힘). git status && rm -rf / 같은 체이닝은 prefix 매칭만으로 못 막으므로 메타문자 전면 거부가 1차 관문. 자격증명 유출·.git 파괴는 되돌릴 수 없는 최악 사고라 보편 블랙리스트가 됐다.

근거 프레임워크. Codex(4중 독립 — OS 샌드박스 + Starlark 실행정책 + 정적 패치판정 + LLM Guardian, 복합명령 분해해 가장 엄격한 규칙 적용) · Claude Code(deny-first + 보호경로 + managed-only MDM 3중) · gajae-code(plan-mode-guard + bash 화이트리스트 + 원본·cd&&제거본 둘 다 검사로 래퍼 우회 차단) · ouroboros(플러그인 신뢰튜플 + digest 재계산으로 코드치환 공격 방어). 70_guardrails-sandbox 공통패턴 1·3·6.

반례. 겹이 많을수록 안전하지만 설치·디버깅·유지보수가 무거워진다 — Codex가 120+ 크레이트로 커진 대가가 이것. 경량 도구는 “우리는 보안 경계가 아니다”라고 솔직히 포기하는 게 낫다(fable-ish). 또 LLM Guardian 겹은 비용·지연·또 다른 인젝션 표면을 추가하므로, 결정적 allow/deny를 먼저 통과시키고 회색지대만 LLM에 보내며 write/network/destructive는 자동 allow 금지로 좁혀야 한다(70 교차검증).


요약 & 셀프체크

3줄 요약:

  1. 7개 하네스의 독립 수렴은 “LLM은 자기보고를 하고·무상태이며·자원이 유한하다”는 제약이 강제한 법칙이다.
  2. 설계의 절반은 모델을 못 믿는 장치(완료 판정·검증자 분리·런타임 강제·deny-first), 나머지 절반은 자원 절약 장치(컨텍스트 예산·위임 격리·위험비례 안전·상태 영속)다.
  3. 모든 원칙의 반례는 같은 결론으로 모인다 — 비가역·고위험에만 최대 강도, 나머지는 가볍게. 균일하게 빡빡하면 하네스가 꺼진다.

스스로 답해보기:

  • “이 작업은 검증이 가능한가?”를 물었을 때, 검증 불가 작업(리서치·토론)에 원칙 1을 강제하면 무슨 부작용이 나는가?
  • 어떤 규칙은 산문(soft)으로 두고 어떤 규칙은 런타임(hard)으로 막아야 하는가 — 그 갈림선은 무엇인가?
  • 보조 훅은 fail-open, 보안 판정은 fail-closed로 반대 방향으로 틀려야 하는 이유를 한 문장으로 설명할 수 있는가?

종합 — 한 문장으로

7개 하네스가 수렴한 모든 법칙의 뿌리에는 한 문장이 있다: “LLM은 똑똑하지만 자기보고를 하고, 무상태이며, 컨텍스트와 권한이 유한하다.” 좋은 하네스 설계의 절반은 “모델을 어떻게 못 믿을 것인가”이고(원칙 1·2·4·10), 나머지 절반은 “유한 자원(컨텍스트·세션·비용)을 어떻게 아낄 것인가”이다(원칙 3·7·8·9). 그리고 둘을 잇는 접착제가 표준 계약과 안전한 루프(원칙 5·6)다. 추상이 아니라 실행으로 가는 다음 한 걸음은 30_개선점/2_통합하네스_설계안20_비교분석/독창아이디어_모음의 이식 후보에 있다.


연결

하네스엔지니어링이란 · HOME · _분석축_루브릭 · _비교매트릭스

관련 횡단노트: 10_agent-loop · 20_context-assembly · 30_hooks · 40_skills-commands · 50_tools-mcp · 60_orchestration · 70_guardrails-sandbox · 80_verification · 90_self-improvement · 100_state-memory

다음 단계: 2_설계_플레이북 · 3_안티패턴 · 4_나만의_하네스_조립가이드


[!tip] Codex 메타검증 (gpt-5.5, xhigh · 검증일 2026-06-15) Codex가 1_하네스_설계원칙.md, 2_설계_플레이북.md, 4_나만의_하네스_조립가이드.md 3개 문서를 실제 harness-work/ 소스와 대조해 비판적으로 교차검증한 결과 요약.

총평 — 세 문서의 방향성은 대체로 타당하다. “자기보고를 증거로 보지 말라 · 검증은 실행흔적·영수증·상태로 남겨라 · 컨텍스트는 예산이다 · 서브에이전트는 격리된 산출 단위다”는 소스와 잘 맞는다. 다만 조립가이드는 몇 군데에서 근거보다 강하게 말한다.

잘 뒷받침되는 원칙 — 자기보고 불신(fable-ish/scripts/verify_state.py:42, stop_gate.py:30, quick/docs-only 예외·최대 2회), 영수증/해시 증거(gajae-code/.../task/receipt.ts:54, harness-control-plane/receipts.ts:1), read-only critic(oh-my-claudecode/agents/critic.md:1), runtime enforcement(codex-rs/sandboxing/src/manager.rs:24), 루프 가드(verify_state.py:11, spawn-gate.ts:1, event_store.py:100).

수정해야 할 주장 — ① “fail-open을 전부 fail-closed로 뒤집자”는 너무 거칠다(OMC는 의도적 fail-open: run.cjs:154, bridge.ts:3021). ② OMC deliverable 검증은 “ADVISORY, non-blocking”이라 강한 gate가 아니다(verify-deliverables.mjs:14). ③ “settings.json hooks 비어있음=무hook”은 절반만 맞다 — 플러그인에 hooks.json이 있다(oh-my-claudecode/hooks/hooks.json:63). ④ 더 직접적 위험은 skipDangerousModePermissionPrompt:true, Codex trusted에 /tmp·system path 포함, Windows sandbox elevated. ⑤ “서브에이전트 재귀 금지”는 Codex v2와 충돌 — 정확히는 “명시적 예산·독립성·산출계약 없이는 금지”(multi_agents_spec.rs:681,720). ⑥ fable-ish Stop gate를 “변경파일↔검증 직결”로 과장 금지(현재는 주로 “성공검증 존재 여부”).

빠진 인사이트 — threat model 분리(안전/품질/비용/유출/우회/기억오염은 다른 실패모드), 관측지표 기록(stop block·false positive·coverage·timeout·bypass·critic reject·token/cache), hook fixture 테스트가 1순위 산출물, PreCompact 자동 wiki/memory 저장의 poisoning·leak 위험, commit trailer는 약한 증거(자동 receipt/hash/ledger 우선), 검증한 소스 commit/date/version 고정 필요.

현실적 조립 순서 — ① 현재 활성 상태(Claude settings·OMC hooks·Codex trust/sandbox) inventory → ② 위험 설정부터 낮추기 → ③ hook fixture 테스트 → ④ destructive·credential/path만 먼저 hard gate → ⑤ Stop gate는 shadow/warn 후 enforce → ⑥ critic 강제는 고위험 변경에만 → ⑦ memory/wiki·self-improve·event sourcing은 마지막.

결론 — 핵심 방향은 좋지만, 조립안은 “강한 통제”보다 **“측정 가능한 통제”**로 바꿔야 한다. 지금 당장 고칠 우선순위는 원칙 재작성이 아니라 실제 설정 audit·hook 테스트·trusted path 축소다.