11.0 KB · 수정 2026-09-10 23:47
목차

ruleguard — 설계·구현계획 (확정 26.9.10)

출처 = [코디] Fable 5 계획 초안 + [GPT] 코덱스 의견 + [페페] 그릴링 결정 11개(handoff.md). 이 문서가 구현의 단일 출처다. 결정 자체는 handoff.md 「확정 결정」.

0. 조사로 확정된 사실

1. 파일

경로 역할
C:\dev\tools\ruleguard\ruleguard.py 엔진 단일 파일. --hook(hook_event_name 분기) · --selftest · --replay <case> · report [--rules] [--health] [--notify] · add-rule · request-off · off --ask · on · unlock --ask · confirm --ask. 장부 I/O·락·세션 상태·transcript 증분 읽기·출력 봉투·지적/자기신고 정규식·태그 파서
C:\dev\tools\ruleguard\detectors\<rule-id>.py 탐지기 1개 = 훅 1개. 엔진이 파일명으로 자동 발견
C:\dev\tools\ruleguard\ledger.json 등록부·시드·승인 기록·enforce_from. 엔진/CLI만 쓴다
(자동) events.jsonl · state\<session_id>.json · logs\hook.log 위반 이벤트(append-only) · 세션 캐시(오프셋·차단 이력) · 예외 로그
편집 C:\Users\delix\.claude\settings.json PreToolUse·UserPromptSubmit·Stop 에 1항목씩 병합(기존 broker·orca·ctxmeter 유지, 백업 먼저)
편집 rules\tooling.md 🆕 · C:\dev\CLAUDE.md §4 태그 문법·명령·해제 절차 1절 + 포인터 1줄

새 의존성 0(stdlib: json·re·os·msvcrt·urllib).

2. 장부 스키마

{ "enforce_from": "<설치+24h, KST ISO>",   // 이 시각 전 = 전 규칙 기록만(롤아웃)
  "guard_from":   "<같은 값>",              // 이 시각부터 장부·엔진·settings 편집 차단
  "rules": { "<rule-id>": { "kind":"auto|judge", "doc":"<파일>#<절제목>",
             "detector":"<rule-id>|null", "dedupe":"prompt_id|session_id|tool_use_id",
             "seed":{"count":N,"dates":[...],"source":"..."},
             "detector_ok":"<selftest 통과 시각|mtime>", "confirmed":null } },
  "approvals": [ {"action":"off|unlock|confirm","target":..,"ask_id":..,"answer":..,"at":..} ],
  "pending":   [ {"id":"P-0001","session_id":..,"prompt_id":..,"excerpt":..,"at":..} ] }

3. 초기 장부 (페페 확정 26.9.10)

id kind 탐지기 seed
notify-missing auto Stop: 작업 턴인데 notify\.py\s+(done|step|ask) 또는 7777/notify- 호출 0 2 (8/2·8/6)
heredoc-file-write auto PreToolUse Bash: << + 실파일 리다이렉트(>·>>·tee, /dev/null 제외) 2 (7/14~9/9)
pfs-git-add-all auto PreToolUse Bash: pfs 리포에서 git add -A|--all|.|<디렉터리>/ 2 (7/13·9/3)
broker-error-retry auto PreToolUse Bash: 이번 턴 직전 broker(7777) 호출이 400/500 이었는데 같은 엔드포인트 재호출 2 (9/5·9/8)
gui-session0 judge PreToolUse Bash: GUI 실행 명령(Start-Process·start ""·chrome/msedge/notepad/explorer 등)인데 schtasks … /IT 경로 아님 → 체크리스트 주입(allow+additionalContext, 세션당 1회) 2 (8/31·9/2) — 페페 확인(confirm) 전까지 미종결
rules-preread-missing auto PreToolUse Write/Edit/MultiEdit/NotebookEdit: 코드·설정 확장자인데 세션 transcript 에 rules[\\/]tooling\.md Read 없음 1 (9/10)
report-tail-missing auto Stop: 작업 턴인데 last_assistant_message 에 시드: 줄 또는 컨텍스트 % 줄 없음 0
session-relay-missed · relative-path-in-report · broker-temp-deny · phone-realdevice-skip judge 없음 각 1

4. 탐지기 인터페이스 (= 「훅 생성」의 정의)

RULE = {"id":..., "events":["PreToolUse"|"Stop"], "tools":[...], "kind":"auto|judge",
        "mode":"deny|checklist", "dedupe":..., "doc":...}
def check(ctx) -> None | {"key":..., "msg":"무엇을 어겼나", "fix":"지금 할 일 한 줄"}
    # ctx.event, ctx.payload, ctx.turn_tool_uses() (tool_use+결과), ctx.session_has(pattern),
    # ctx.last_text (Stop만), ctx.state
def selftest() -> [(이름, bool), ...]   # 양성·음성 최소 2쌍

5. 페페 지적·자기신고

6. 출력

상황 출력(exit 0)
PreToolUse 위반·block permissionDecision:"deny" + 사유(규칙 id·횟수·고칠 방법·해제는 request-off)
PreToolUse 위반·record 없음(기록만)
체크리스트(judge) permissionDecision:"allow" + additionalContext
Stop 위반·block(stop_hook_active false, 이 prompt_id 첫 차단) {"decision":"block","reason":…}
Stop nag(탐지기 없는 규칙 count>=2) 턴당 1회 block, 「detectors\X.py 생성 → --selftest」
UserPromptSubmit 위반·pending 있을 때만 [규칙장부] … additionalContext(없으면 무출력)
내부 예외 무출력 exit 0 + logs\hook.log

7. 리스크 대책

8. 페페 승인 해제와 우회 차단

  1. ruleguard.py request-off <rule> "사유" → /ask 발송(본문에 「답장: 해제 또는 유지」) → ask_id 출력.
  2. 페페 텔레그램 해제.
  3. ruleguard.py off <rule> --ask ASK-… → GET /ask/<id> 서버측 검증(answered · 질문에 rule id · 답이 해제로 시작) → approvals 기록 → 그 규칙만 off. on 은 승인 불필요. - 가드(guard_from 이후): tools\ruleguard 하위 기존 파일 Edit/Write → deny(신규 detectors 만 허용) · Bash 에 ruleguard|ledger.json|events.jsonl + 쓰기 동사 → deny(ruleguard.py <명령> 은 통과) · settings.json 에서 ruleguard 항목 제거 시도 → deny. 정당한 수정은 unlock <경로> --ask(30분 창).

9. 롤아웃 (페페 결정 = 하루 기록 모드 후 차단)

10. 검증

  1. --selftest: 락·원자쓰기 / count=seed+distinct key / mode 전이 / 태그 파서 / 지적·자기신고 정규식 양성 6·음성 6(「누락 검사 완료」 같은 정상 문장 제외) / 출력 JSON / 깨진 stdin·없는 transcript → 무출력 exit 0 / 각 탐지기 selftest / 가드 경로 판정 / RULEGUARD=off.
  2. --replay <case>: 스크립트가 내부에서 합성 입력을 만들어 자기 --hook 경로로 돌린다(heredoc·파이프 안 씀). 기대 JSON 정확 비교.
  3. 성능: 실제 대형 transcript 로 PreToolUse <200ms · Stop <1s 실측.
  4. 실세션(기록 모드): settings 병합 후 실제 트리거 → events 1줄 + transcript hook_success 확인.
  5. 해제 왕복·차단 실측은 enforce_from 이후.