한 줄 요약
조사·검증 절차(procedure)와 소스 라이선스 분류(source policy)를 데이터로 선언하고, 위반을 fail-closed 게이트로 막는 컴포넌트다. 왜 배우나 - “잘했다”는 말 대신 측정 가능한 증거를, “써도 되나”는 직감 대신 보수적 규칙으로 강제해 작업의 안전선을 코드로 고정하기 위해서다.
그림
flowchart TD subgraph PROC["procedure.py - 절차 프로파일"] P1["ProcedureProfile<br/>4개 모듈"] P1 --> M1["verification grounding<br/>증거 없는 완료선언 금지"] P1 --> M2["investigation protocol<br/>범위/증거/결정 분리"] P1 --> M3["multi-pass review<br/>구조/안전/완전성 분리검사"] P1 --> M4["drift prevention<br/>원래 목표에 고정"] M1 & M2 & M3 & M4 --> RM["render_markdown()<br/>체크리스트 문서 출력"] end subgraph SRC["source_policy.py - 소스 정책"] S0["Source(name, url, license, classification, risk)"] S0 --> CL{"classify()"} CL -->|"is_leaked=True"| R1["reference-only / high"] CL -->|"license unknown"| R2["unverified / high"] CL -->|"MIT/Apache/BSD..."| R3["adapt / low"] CL -->|"그 외 식별가능"| R4["reference-only / medium"] R1 & R2 & R3 & R4 --> AUD{"audit(ledger)"} AUD -->|"위반 0건"| OK["GateResult passed=True"] AUD -->|"non-public을 copy/adapt<br/>또는 blocked 존재<br/>또는 미지 값"| NG["GateResult passed=False<br/>reasons 누적"] end
쉽게 풀기
이 컴포넌트는 두 개의 규칙집을 합쳐 놓은 것이다.
첫째, 절차(procedure)는 “일하는 자세 체크리스트”다. 호텔 청소부가 “침대 정리 - 욕실 소독 - 비품 보충”을 순서표로 들고 다니듯, AI 작업자도 4가지 묶음의 체크리스트를 들고 다닌다. (1) 완료라고 말하려면 명령어 출력이나 파일 경로 같은 증거를 붙여라, (2) 손대기 전에 기존 파일부터 살펴보고 왜 이 길을 골랐는지 적어라, (3) 만들기와 검사하기를 따로 나눠서 구조-안전-완전성 세 번 훑어라, (4) 길을 잃지 않게 처음 목표와 받아들임 기준을 계속 대조하라. 이 체크리스트는 사람이 읽도록 마크다운 문서로 뽑아낼 수 있다.
둘째, 소스 정책(source policy)은 “남의 자료 갖다 쓰기 안전등급표”다. 식당이 식재료를 받을 때 원산지와 유통기한을 확인하듯, 외부 코드/프롬프트를 받을 때 출처와 라이선스를 확인한다. 핵심은 “의심스러우면 막는다”(fail-closed)는 보수적 태도다.
- 유출(non-public) 자료다 -> 통째로 복사 금지, 참고만(reference-only), 위험 high.
- 라이선스를 모르겠다 -> 검증 전엔 못 쓴다(unverified), 위험 high.
- MIT 같은 관대한 라이선스다 -> 변형해 써도 된다(adapt), 위험 low.
- 라이선스는 알겠지만 관대하진 않다 -> 보수적으로 참고만(reference-only), 위험 medium.
마지막에 audit이 전체 목록(ledger)을 훑어, 유출 자료를 복사로 분류했거나 차단(blocked) 항목이 섞였거나 정의되지 않은 값이 나오면 “통과 실패”로 막아준다.
핵심 정리
| 영역 | 핵심 객체/함수 | 하는 일 | 안전 장치 |
|---|---|---|---|
| 절차 | ProcedureProfile / render_markdown() | 4개 모듈 체크리스트를 묶고 문서로 출력 | 모듈마다 purpose+checklist 고정 |
| 분류 | classify() | 유출/라이선스로 5종 분류 결정 | unknown은 unverified로 차단 |
| 위험 | classify_risk() | 분류값을 low/medium/high로 매핑 | 미지 분류는 high로 보수 처리 |
| 감사 | audit() | 원장 전체 위반 검사 | 위반 시 fail-closed (passed=False) |
5가지 분류값과 위험 매핑 전체
- 분류 CLASS =
("copy", "adapt", "reference-only", "blocked", "unverified")- 위험 _RISK =
("low", "medium", "high")classify: is_leaked=True -> reference-only / unknown license -> unverified / permissive(mit·apache·bsd·isc·unlicense) -> adapt / 그 외 -> reference-onlyclassify_risk: leaked -> high / unverified -> high / reference-only -> medium / adapt -> low / blocked -> high / 미지 -> high- unknown 토큰: unknown, 불명확, 불명, 모호, none, n/a, na, tbd
audit 가 fail 시키는 3가지 위반과 _is_leaked_like 판정
- 위반1: non-public 성격 소스를 copy 또는 adapt로 분류 -> fail
- 위반2: classification == “blocked” 가 빌드에 존재 -> fail
- 위반3: CLASS/_RISK 에 없는 알 수 없는 값 -> fail (fail-closed)
_is_leaked_like: license에 “non-public”/“leaked” 포함, 또는 (classification"reference-only" and risk”high”)면 유출 성격으로 본다.- 기본 원장(
default_ledger)은 ATTRIBUTION.md의 10개 소스(MIT 4개, unknown 4개, non-public 2개)를 데이터로 표현.
실제 예시
# fablelayer/source_policy.py (분류 핵심 로직)
def classify(name: str, license: str, is_leaked: bool) -> str:
if is_leaked:
return "reference-only" # non-public: 전문 포함 금지
if _is_unknown_license(license):
return "unverified" # fail-closed: 검증 전 채택 금지
if _is_permissive_license(license):
return "adapt" # MIT/Apache/BSD...
return "reference-only" # 그 외 식별 가능 -> 보수적# fablelayer/source_policy.py (원장 감사 = fail-closed 게이트)
def audit(ledger: tuple[Source, ...]) -> GateResult:
reasons: tuple[str, ...] = ()
for src in ledger:
if src.classification not in CLASS:
reasons = reasons + (f"{src.name}: invalid classification '{src.classification}'",)
continue
if src.risk not in _RISK:
reasons = reasons + (f"{src.name}: invalid risk '{src.risk}'",)
if _is_leaked_like(src) and src.classification in ("copy", "adapt"):
reasons = reasons + (f"{src.name}: non-public source must not be copy/adapt ...",)
if src.classification == "blocked":
reasons = reasons + (f"{src.name}: blocked source present in build",)
return GateResult(passed=len(reasons) == 0, reasons=reasons)# fablelayer/procedure.py (절차 프로파일 한 모듈 예)
ProcedureModule(
name="verification grounding",
purpose="Do not claim completion without measurable evidence.",
checklist=(
"Attach command output, file path, or source URL to every completion claim.",
"Mark untested behavior as unverified.",
"Prefer fail-closed gates over narrative confidence.",
),
)# 사용 흐름 예시 (의사 코드)
# from fablelayer.source_policy import default_ledger, audit
result = audit(default_ledger())
assert result.passed is True # 기본 원장은 정책을 위반하지 않도록 구성됨
# from fablelayer.procedure import default_profile
doc = default_profile().render_markdown() # 절차 체크리스트를 .md 문서로요약 & 셀프체크
3줄 요약:
- procedure.py는 4개 모듈(검증근거·조사프로토콜·다중패스리뷰·드리프트방지)의 체크리스트를 데이터로 선언하고 마크다운으로 렌더한다.
- source_policy.py는 유출 여부와 라이선스로 소스를 5종(copy/adapt/reference-only/blocked/unverified)으로 분류하고 위험등급을 매긴다.
- audit()이 non-public을 copy/adapt로 분류했거나 blocked가 섞였거나 미지 값이 있으면 fail-closed로 막는다.
셀프체크:
- 라이선스가 “TBD”인 소스는
classify에서 어떤 분류가 되고, 그 이유(설계 의도)는 무엇인가? - classification이 “reference-only”이고 risk가 “high”인 소스를 누군가 “adapt”로 바꾸면 audit은 어떻게 반응하는가? (
_is_leaked_like기준으로 설명) - 절차의 “multi-pass review”가 만들기와 검사를 분리하는 이유를, 소스 정책의 fail-closed 태도와 연결해 설명하라.