Harness Engineering · Part 4 · Chapter 15

제안과 제약은
전혀 다른 것이다

“하지 말라”고 말하는 것과 “할 수 없게” 만드는 것은 같은 문장이 아니다. 하나는 기억과 판단에 기대고, 다른 하나는 구조와 실행에 기대어 선다. Harness가 취향의 기술에서 공학으로 넘어가는 지점은 바로 이 차이에 있다.

대상: 원문 §15 「约束层:建议和约束是两回事」 범위: PDF 75–82쪽 83쪽부터 §16 시작 외부 자료 미사용
규칙은 생각을 도와준다. 제약은 바닥을 지켜준다. 모든 것을 쇠사슬로 묶을 필요는 없지만, 무너져서는 안 되는 곳을 말로만 지키는 것도 공학은 아니다.
01

근본적인 구분 — “그러지 마라”와 “그렇게 할 수 없다”

CLAUDE.md에 “main 브랜치로 직접 push하지 말라”고 쓰면 그것은 지속적인 지시지만 여전히 제안이다. Agent는 대체로 따르지만, 컨텍스트가 길거나 작업이 복잡하거나 모델이 흔들리는 순간 무시할 수 있다. 같은 위험을 프로그램 수준에서 차단하면 성격이 달라진다.

Suggestion

모델의 판단에 맡기는 규칙

지시 파일은 지속적으로 읽히지만 실행 여부는 모델의 행동에 달려 있다. 코딩 스타일, 선호, 아키텍처 방향처럼 어겨도 복구 가능한 규칙에 적합하다.

- main 브랜치에 직접 push하지 않는다.
지속적 유연함 우회 가능 컨텍스트 영향 받음
VS
Constraint

프로그램이 실행 자체를 막는 제약

도구 호출 전에 matcher가 위험한 명령을 잡고 deny를 반환하면 Agent가 어떤 판단을 하더라도 해당 행동은 실행되지 않는다. 안전선, 생산 환경, 비가역 작업에 적합하다.

PreToolUse → match 위험 명령 → DENY
결정론적 강제적 우회 불가 프로그램 집행

원문의 핵심 예시: main push 차단

Claude Code의 Hook은 도구 실행 전 개입할 수 있다. 원문은 PreToolUse에서 Bash(git push*main*)을 감지하고 명령을 거절하는 설정을 예로 든다.

  • 지시 파일: “하지 않는 편이 좋다”
  • Hook: 실행 직전에 프로그램이 차단
  • 컨텍스트가 흔들려도 정책은 유지
  • 원문이 말하는 art → engineering의 분기점

.claude/settings.json · 원문 구조를 단순화한 예시

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash(git push*main*)",
      "handler": {
        "type": "shell",
        "command": "echo 'DENY: main 직접 push 금지'"
      }
    }]
  }
}
제안은 Agent의 지능에 기대고, 제약은 시스템의 구조에 기대어 선다. 중요한 것은 어느 쪽이 더 우월한지가 아니라 어떤 실패를 사람의 판단에 맡기고, 어떤 실패를 애초에 불가능하게 만들 것인가다.
02

Claude Code Hooks — 행동의 전후에 프로그램을 삽입한다

원문은 2026년 4월의 Claude Code v2.1.90을 기준으로 20개가 넘는 lifecycle event가 있고, 그중 일곱 개를 가장 자주 쓰는 Hook으로 정리한다. Hook은 대화의 조언이 아니라 실행 경로의 특정 지점에 끼어드는 프로그램이다.

Event 언제 발생하는가 원문이 제시한 핵심 역할
PreToolUse 도구 호출 전 작업 deny · 안전 정책의 핵심 집행점 · defer 결정으로 headless 세션을 멈췄다가 재개
PostToolUse 도구 호출 후 감사, 로그, 자동 formatting
SessionStart 세션 시작 동적 컨텍스트 로딩, 환경 초기화
Stop Agent 종료 시 결정론적 완료 검사
PermissionRequest 권한 요청 시 승인 자동화, Slack 등의 승인 경로로 라우팅
PermissionDenied 자동 모드에서 권한이 거절된 뒤 거절 동작 기록, 대체 경로 트리거
PostCompact 컨텍스트 압축 후 압축 이벤트에 반응
PreToolUse

원문이 가장 중요하게 보는 Hook이다. 도구가 실행되기 전에 deny를 반환해 특정 행동을 프로그램 수준에서 차단한다. 제약이 지시를 넘어 실제 집행으로 바뀌는 지점이다.

Hook은 Agent에게 더 많은 말을 건네는 장치가 아니다. 말이 실패해도 지켜져야 하는 조건을 실행 경로 안에 새기는 장치다.
03

네 가지 실용 Hook — 반복적인 검증과 위험 차단을 자동화한다

원문은 Hook이 추상적인 안전 기능이 아니라 일상적인 개발 피드백 루프를 만드는 도구임을 네 사례로 보여준다. 편집 뒤 검사하고, 위험한 파일을 보호하고, 타입 오류를 즉시 드러내고, commit 전에 테스트를 통과하게 한다.

PostToolUse

파일 편집 뒤 자동 lint

Edit|Write 이후 ESLint를 실행한다. 실패 출력을 Agent가 바로 읽어 수정하도록 만들어 사람이 별도로 “lint 돌려라”라고 반복하지 않는다.

after Edit / Write
→ eslint 실행
→ 오류 출력 반환
→ Agent가 즉시 수정
PreToolUse

생산 환경 설정 파일 보호

.env*나 production 설정에 대한 직접 편집을 도구 호출 전에 막는다. 안전선은 문서에 적어두는 것으로 끝내지 않는다.

match Edit(.env*)
or Edit(*.production.*)
→ DENY
PostToolUse

TypeScript 편집 뒤 자동 type check

.ts, .tsx 파일이 바뀌면 즉시 타입 검사를 수행한다. Agent가 오류를 보고 스스로 고치므로 수동 확인이 피드백 루프에서 빠진다.

after Edit(*.ts / *.tsx)
→ tsc --noEmit
→ type error feedback
PreToolUse

commit 전 테스트 통과 요구

git commit 직전에 테스트를 실행해 실패한 상태로 제출하는 것을 막는 예다. 완료 선언보다 검증을 먼저 통과시키는 구조다.

before git commit
→ test suite 실행
→ 실패 시 진행 차단
원문은 재미있는 세부로 “Agent에게 Hook 자체를 작성하게 할 수 있다”고 덧붙인다. 즉 Agent가 자신의 자유도를 제한하는 프로그램을 스스로 만들게 할 수 있다. 자동화가 자기 제약을 설계하는 장면이다.
반복해서 부탁하는 검증은 부탁이 아니라 시스템으로 옮길 후보가 된다. 사람이 매번 기억해야 하는 절차를 Hook으로 옮길수록 품질은 개인의 주의력보다 구조에 가까워진다.
04

OpenAI의 Hard Constraint — 아키텍처를 규칙이 아니라 기계적 경계로 만든다

원문은 Codex CLI에는 Claude Code와 같은 Hook이 없다고 설명한 뒤, OpenAI 팀이 다른 방식의 기계적 강제를 사용했다고 소개한다. 핵심은 “싫은 코드의 성질을 말로 설명할 수 있다면 다음 단계는 그것을 규칙과 검사로 바꾸는 것”이라는 철학이다.

Layer 01

의존성 계층 아키텍처

비즈니스 영역 안에서 코드가 한 방향으로만 의존하도록 고정한다. UI는 Service를 의존할 수 있지만 Service는 UI를 거꾸로 참조할 수 없다.

TypesConfigRepo ServiceRuntimeUI
인증·telemetry·Feature Flags 같은 cross-domain concern은 Providers라는 하나의 명시적 인터페이스로 들어온다.
Layer 02

Custom Linter + Structural Test

Codex가 자체 ESLint 규칙과 구조 테스트를 생성해 계층 위반을 탐지한다. 오류 메시지는 위반 사실만 말하지 않고 수정 방향과 문서 링크까지 제공해 Agent가 스스로 고칠 수 있게 한다.

Layer 03

CI가 merge를 물리적으로 막는다

앞선 검사들은 CI에서 실행되고, 위반한 PR은 주 브랜치에 합쳐질 수 없다. 규칙은 조언이 아니라 저장소의 실제 통과 조건이 된다.

원문은 원래 수백 명의 엔지니어가 생긴 뒤에야 세우곤 하는 강한 아키텍처가 coding agent 환경에서는 초기 전제 조건으로 앞당겨진다고 해석한다. Agent가 늘어날수록 일관성을 사람의 자각에 맡기기 어려워지기 때문이다.

빠른 생성은 아키텍처를 덜 필요하게 만드는 것이 아니다. 오히려 반대다. 코드를 만드는 속도가 빨라질수록 일관성을 유지하는 제약은 더 일찍 필요해진다.
05

Codex Sandbox — “하지 마라”보다 “할 수 없다”가 더 단순하다

Codex CLI의 또 다른 Hard Constraint는 sandbox다. 프로그램 로직으로 매 행동을 판정하기보다 Agent가 접근할 수 있는 파일과 네트워크의 범위를 환경 수준에서 제한한다.

원문이 제시한 두 모드

기본 모드는 작업공간 안에서만 쓰기를 허용하고 네트워크를 닫는다. 더 넓은 권한 모드는 전체 파일 시스템과 네트워크를 연다.

workspace-write · default 작업공간만 쓰기 가능 · 네트워크 닫힘
danger-full-access 전체 디스크 쓰기 가능 · 네트워크 열림

환경 수준의 제약

원문은 기본 모드에서 Agent가 작업 디렉터리 밖을 마음대로 쓰지 못하고 네트워크도 사용할 수 없다고 설명한다. 또한 .git/.codex/가 보호된다고 서술한다. 핵심은 규칙을 설명하는 것이 아니라 환경이 가능성 자체를 줄이는 데 있다.

.git/ 보호 .codex/ 보호 Network off · default Workspace boundary
가장 단순한 제약은 행동을 설득하는 것이 아니라 행동의 가능성을 없애는 것이다. Agent가 할 수 없는 일은 잊어버리거나 오해해서 실행할 수도 없다.
06

제약의 스펙트럼 — 부드러운 언어에서 물리적 격리까지

원문은 모든 제약 수단을 한 줄 위에 놓는다. 오른쪽으로 갈수록 신뢰성은 높아지지만 유연성은 줄어든다. 좋은 Harness는 한 종류만 고집하지 않고 손실의 크기에 따라 다른 강도를 배치한다.

Prompt Reminder일회성 · 사용자
Rule File지속적 · 모델 준수
Hooks강제 · 프로그램
Linter강제 · 정적 분석
CI강제 · 통합 시스템
Sandbox물리적 · OS / 정책
더 유연함 · 우회 가능 더 신뢰할 수 있음 · 더 경직됨
제약 방식 성격 집행자 우회 가능성 원문이 연결한 도구
대화 속 구두 알림 즉시 · 일회성 사용자 있음 모든 도구
CLAUDE.md / AGENTS.md 지속적 · 제안적 모델 있음 모든 도구
Hook · PreToolUse deny 지속적 · 강제적 프로그램 없음 Claude Code
Custom Linter 지속적 · 강제적 정적 분석 없음 도구 독립 · 직접 구축
CI 차단 지속적 · 강제적 CI 시스템 없음 도구 독립 · 직접 구축
Sandbox 격리 지속적 · 물리적 운영체제 / 정책 없음 Codex CLI
모든 규칙을 가장 단단한 벽으로 만들면 Harness는 안전하지만 숨을 쉬기 어렵다. 모든 규칙을 말로만 남기면 자유롭지만 신뢰하기 어렵다. 좋은 Harness는 자유와 안전의 비율을 규칙마다 다르게 정한다.
07

어디까지 강제할 것인가 — 규칙이 아니라 결과의 크기를 묻는다

원문은 한 규칙을 제안으로 둘지 제약으로 올릴지 판단하는 단순한 기준을 제시한다. “Agent가 이 규칙을 어겼을 때 어떤 일이 일어나는가?” 손실이 작으면 유연성을 남기고, 손실이 크면 하드한 경계로 올린다.

Suggestion이면 충분한 경우

일관성이 조금 깨지거나 리뷰에서 쉽게 수정할 수 있는 문제다. 유연성을 버리고 강제할 이유가 크지 않다.

변수 이름이 마음에 들지 않음 · 리뷰에서 수정 가능
코딩 스타일 불일치 · 성가시지만 치명적이지 않음
일반 아키텍처 방향 · 판단 여지를 남겨야 할 수 있음

Constraint로 올려야 하는 경우

복구 비용이 크거나 비가역적이거나 안전 문제로 이어지는 행동이다. 사람의 기억이나 모델의 준수율에 맡기지 않는다.

생산 DB 삭제 · 재난적 결과 → 반드시 제약
main 직접 push · 되돌릴 수 있어도 운영 비용 큼 → 제약
migration 파일 삭제 · Hook / CI / Sandbox로 차단

원문이 남기는 문장은 짧다.
제약이 지켜야 하는 것은 취향이 아니라 바닥선이다.

무엇을 강제로 만들 것인지 결정하는 순간, Harness는 단순한 설정 파일 모음에서 위험을 분류하는 시스템이 된다. 규칙의 강도는 중요성의 감정이 아니라 실패했을 때의 결과로 정한다.
08

제약은 전환점이다 — 자유를 없애는 것이 아니라 신뢰할 수 있게 만든다

장의 마지막은 도구별 제약 철학을 비교한다. 원문 시점에서 Claude Code는 프로그래밍 가능한 Hook, Codex CLI는 정책형 Sandbox를 하드 제약으로 제시하고, Cline은 Plan/Act 분리와 사람 승인으로 사회적 제약을 만든다고 설명한다.

Claude Code · Programmable Constraint

Hook 안에 임의 로직을 작성해 approve / deny를 결정한다. 상황별 정책을 프로그램으로 표현할 수 있다는 점이 핵심이다.

Codex CLI · Policy Constraint

Sandbox의 미리 정의된 권한 수준을 선택한다. 임의의 커스텀 로직이라기보다 환경의 접근 가능 범위를 정책으로 고정한다.

Cline · Social Constraint

Plan 모드는 분석만 하고 수정하지 않으며, Act 모드에서는 각 단계에 인간 승인을 요구한다. 기술적으로 막기보다 과정 안에 사람의 확인을 넣는다.

원문은 해법 공간을 제한할수록 오히려 Agent를 더 신뢰할 수 있다는 분석으로 끝난다. 규칙이 없는 조직이 자유로운 조직이 아니라 혼란스러운 조직인 것처럼, Agent도 경계가 선명할수록 그 안에서는 더 과감하게 움직일 수 있다.

Harness는 AI의 힘을 줄이는 장치가 아니다. 그 힘을 신뢰할 수 있는 형태로 바꾸는 장치다.

자유에는 언제나 경계가 있다.
경계가 없어서 자유로운 것이 아니라
넘어서는 안 되는 곳이 분명하기 때문에
그 안에서 더 멀리 갈 수 있다.

지시 파일은 방향을 말하고,
Hook은 행동을 막고,
Linter와 CI는 질서를 확인하며,
Sandbox는 세계의 크기 자체를 정한다.

중요한 것은 모든 것을 금지하는 일이 아니다.
무엇만은 절대로 무너지지 않아야 하는지 합의하는 일이다.

Harness의 공학은 바로 그 바닥선을
사람의 기억에서 시스템의 구조로 옮기는 데서 시작한다.

이 웹페이지는 첨부 문서 《Harness Engineering》의 §15 「约束层:建议和约束是两回事 / The Constraint Layer: Suggestions vs. Enforcement」 (PDF 75–82쪽)에만 근거해 재구성했다. 83쪽에서 §16이 시작되는 것을 확인해 15장의 범위를 분리했다. 지시와 제약의 차이, Claude Code Hooks의 lifecycle event, 네 가지 Hook 활용 예, OpenAI의 의존성 계층·Custom Linter·CI 강제, Codex Sandbox, 소프트→하드 제약 스펙트럼, 규칙의 위반 결과에 따른 제약 강도 판단, 도구별 제약 철학과 장의 마지막 결론을 모두 포함했다. 제품 버전·도구 지원 범위·기능 상태는 외부에서 갱신하지 않고 원문 시점의 서술로만 다뤘다.