한 줄 요약

FableLayer는 모델 출력의 “조기 종료” 패턴을 종료 코드로만 차단하는 bash hook(early-stop.sh)과, 그 hook을 포함한 절차/구조 레이어를 Claude Code에 배포하는 플러그인 매니페스트(plugin.json, marketplace.json)로 구성된다. 왜 배우나: 산문 경고가 아니라 exit code로 약속-미실행과 범위 축소를 결정론적으로 막는 방법, 그리고 그 규율 레이어를 어떻게 설치 가능한 단위로 묶는지를 직접 만들 수 있게 하기 위해서다.

그림

flowchart TD
    A["모델 출력 텍스트"] --> B["early-stop.sh<br/>입력 수집: 인자 / --file / stdin"]
    B --> C{"입력 비어 있나?"}
    C -->|"예"| E1["exit 1 (사용법 오류)"]
    C -->|"아니오"| D["1) 범위 축소 패턴 검사<br/>SCOPE_REDUCTION_PATTERNS"]
    D -->|"매치"| BLOCK["exit 2 (차단, fail-closed)"]
    D -->|"미매치"| F["2) 약속 패턴 검사<br/>PROMISE_PATTERNS"]
    F -->|"미매치"| PASS["exit 0 (통과)"]
    F -->|"매치"| G{"완료-증거 패턴<br/>EVIDENCE_PATTERNS 있나?"}
    G -->|"있음"| PASS
    G -->|"없음"| BLOCK

    subgraph PLUGIN["플러그인 패키징"]
        P1["plugin.json<br/>layers: procedure_harness=agents/ + hooks/"]
        P2["marketplace.json<br/>plugins[].source = ./"]
        P2 --> P1
    end
    BLOCK -.-> P1
    PASS -.-> P1

쉽게 풀기

조기 종료(early-stop)란, AI가 “이제 보고서를 작성하겠습니다”라고 말만 하고 실제로는 작성하지 않은 채 끝내거나, “시간 관계상 나머지는 생략하겠습니다”처럼 시킨 일의 범위를 제멋대로 줄이는 행동이다. 사람으로 치면 “다음 주에 할게요”라고 약속하고 자리를 뜨는 것과 같다.

early-stop.sh는 이 두 가지 나쁜 습관을 잡아내는 자동 검사관이다. 검사 절차는 단순하다.

  1. 검사할 텍스트를 받는다. 방법은 세 가지다. 따옴표로 직접 넘기거나, --file 경로로 파일을 읽게 하거나, 파이프(|)로 흘려보낸다.
  2. 먼저 “범위를 줄이는 말”이 있는지 본다. “이하 생략”, “여기까지만”, “omitted for brevity” 같은 표현이 하나라도 보이면 변명의 여지 없이 즉시 차단한다.
  3. 다음으로 “하겠습니다 / I will / let me” 같은 미래 약속 표현이 있는지 본다. 약속만 있고 실제로 한 흔적(“완료했습니다”, “테스트 통과”, “exit 0” 같은 증거 표현)이 전혀 없으면 차단한다. 약속과 증거가 함께 있으면 통과시킨다.

핵심은 이 검사관이 말로 “위험합니다”라고 경고하지 않는다는 점이다. 대신 종료 코드라는 숫자 신호만 남긴다. 통과는 0, 차단은 2, 사용법 오류는 1이다. 이 숫자는 거짓말을 할 수 없으므로, 상위 시스템은 산문을 읽고 해석할 필요 없이 숫자만 보고 다음 단계로 갈지 멈출지 결정한다(fail-closed: 의심스러우면 막는다).

플러그인 구조는 이 검사관을 비롯한 여러 규율 도구를 한 상자에 담아 Claude Code에 설치할 수 있게 만든 포장지다. plugin.json이 상자 안에 무엇이 들었는지(스킬, 에이전트, hook 폴더)를 적은 내용물 목록이고, marketplace.json은 그 상자를 가게 진열대에 올리는 라벨이다.

핵심 정리

구성요소역할판정/식별 방식
hooks/early-stop.sh조기 종료(약속-미실행, 범위 축소) 감지종료 코드(0/1/2)로만
범위 축소 패턴명시 범위 임의 축소 차단매치 즉시 exit 2
약속 패턴 + 증거 패턴약속만 있고 증거 없으면 차단약속 매치 & 증거 미매치 → exit 2
plugin.json플러그인 내용물 매니페스트name/version/skills/agents/layers
marketplace.json마켓 등록 라벨plugins[].source = ”./“
종료 코드의미발생 조건
0통과조기 종료 패턴 없음
2차단범위 축소 매치, 또는 약속만 있고 증거 없음
1사용법 오류입력 없음 / 파일 없음 / 빈 입력

실제 예시

조기 종료 감지 hook의 핵심 판정부다. 범위 축소는 증거와 무관하게 즉시 차단하고, 약속은 증거가 없을 때만 차단한다.

# 파일경로: /home/seunghyeong/harness-fablelayer/hooks/early-stop.sh (127~162행)
 
# 1) 범위 축소 패턴: 발견 즉시 차단(증거와 무관하게 명시적 범위 축소는 위반).
if [ "${#SCOPE_REDUCTION_PATTERNS[@]}" -ne 0 ]; then
  for pat in "${SCOPE_REDUCTION_PATTERNS[@]}"; do
    if has_match "$INPUT" "$pat"; then
      printf 'BLOCK: scope-reduction pattern detected: %s\n' "$pat" >&2
      exit 2
    fi
  done
fi
 
# 2) 약속-미실행 패턴: 약속 표현이 있고 완료-증거 표현이 전혀 없으면 차단.
PROMISE_HIT=""
if [ "${#PROMISE_PATTERNS[@]}" -ne 0 ]; then
  for pat in "${PROMISE_PATTERNS[@]}"; do
    if has_match "$INPUT" "$pat"; then
      PROMISE_HIT="$pat"
      break
    fi
  done
fi
 
if [ -n "$PROMISE_HIT" ]; then
  EVIDENCE_HIT=""
  if [ "${#EVIDENCE_PATTERNS[@]}" -ne 0 ]; then
    for pat in "${EVIDENCE_PATTERNS[@]}"; do
      if has_match "$INPUT" "$pat"; then
        EVIDENCE_HIT="$pat"
        break
      fi
    done
  fi
  if [ -z "$EVIDENCE_HIT" ]; then
    printf 'BLOCK: promise-without-execution detected: %s (no completion evidence)\n' "$PROMISE_HIT" >&2
    exit 2
  fi
fi
 
exit 0

세 가지 입력 방식과 기대 종료 코드:

# 직접 인자: 범위 축소 → exit 2 차단
early-stop.sh "시간 관계상 나머지는 생략하겠습니다"
 
# 약속만 있고 증거 없음 → exit 2 차단
echo "이제 보고서를 작성하겠습니다" | early-stop.sh
 
# 약속 + 완료 증거 동반 → exit 0 통과
early-stop.sh "보고서를 작성했습니다. 테스트 통과(exit 0)"
 
# 파일 검사
early-stop.sh --file ./model_output.txt

플러그인 매니페스트의 레이어 매핑부다. hook은 procedure_harness 레이어에 묶인다.

// 파일경로: /home/seunghyeong/harness-fablelayer/.claude-plugin/plugin.json (29~50행)
"claude_code": {
  "min_version": "1.0.0",
  "skills_dir": "./.claude/skills",
  "agents_dir": "./agents"
},
"layers": {
  "promptcore": "./core",
  "procedure_harness": "./agents",
  "value_optimizer": "./styles",
  "skillpack_router": "./skills"
},
"layers_note": "Layer directories are populated by the FableLayer build (modes: new, layer 1-4). promptcore=core/, procedure_harness=agents/ + hooks/, value_optimizer=styles/, skillpack_router=skills/. The plugin packages whichever layers exist at install time and does not synthesize missing layer content."

마켓 등록 라벨이다. 플러그인 소스를 현재 디렉터리(./)로 가리킨다.

// 파일경로: /home/seunghyeong/harness-fablelayer/.claude-plugin/marketplace.json (11~16행)
"plugins": [
  {
    "name": "fablelayer",
    "source": "./",
    "description": "Discipline layer (not a capability transplant): verification grounding, completion evidence gate, systematic investigation, early-stop prevention, and value output style packaged for Claude Code, CLI, and local LLM adapters. Performance claims are deferred to bench/RESULTS.md.",
    "version": "0.1.1"
  }
]

요약 & 셀프체크

3줄 요약:

  1. early-stop.sh는 범위 축소 패턴은 즉시, 약속-미실행 패턴은 완료 증거가 없을 때만 차단하며, 판정은 오직 종료 코드(0 통과 / 2 차단 / 1 사용법 오류)로 한다.
  2. 설계 원칙은 fail-closed와 결정론이다. 산문 경고를 신뢰 근거로 쓰지 않고, grep -Eq로 매치 여부만 보며, bash 3.2 안전 가드와 LC 고정으로 한글 크래시를 막는다.
  3. plugin.json은 hook을 procedure_harness 레이어(agents/ + hooks/)로 묶어 패키징하고, marketplace.json은 그 플러그인을 source: ./로 마켓에 등록한다. 단, 빌드 시 존재하는 레이어만 패키징하며 없는 레이어 내용을 합성하지는 않는다.

셀프체크:

  1. “이제 구현하겠습니다”만 있는 출력과, “이제 구현하겠습니다. 구현했습니다.”가 있는 출력의 종료 코드는 각각 무엇이며 왜 다른가?
  2. 범위 축소 패턴이 약속 패턴보다 먼저, 그리고 증거와 무관하게 차단되도록 설계한 이유는 무엇인가?
  3. plugin.json의 layers_note는 “없는 레이어 내용을 synthesize하지 않는다”고 명시한다. 이 문장이 플러그인 설치 동작에 대해 보장하는 것은 무엇인가?

연결

FL_개요 · _분석축_루브릭