검증과 게이트
발행 직전에 글은 두 게이트를 직렬로 통과합니다. Phase 3의 품질 판정과 Phase 4의 AI-smell 점수입니다. 둘 다 통과해야 Blogger 업로드에 닿습니다. 여기서는 각 게이트가 무엇을 보는지와, 그 아래에 깔린 보안 기준선을 봅니다.
Phase 3 — quality-check 네 축
섹션 제목: “Phase 3 — quality-check 네 축”quality-check는 한글본과 영문본을 같이 읽고 네 축으로 판정합니다. 보고서는 _workspace/briefs/{slug}-quality.md에 떨어집니다.
- AdSense 정책 — 저가치 콘텐츠 신호, 정책 위반 가능성. 별도
adsense-optimization검수와 짝을 이룹니다. - SEO — 메타 태그, 스키마, hreflang 상호 참조, 키워드 밀도.
- E-E-A-T — 경험, 전문성, 권위, 신뢰 신호. 1인칭 경험과 출처 인용이 실제로 들어있는지.
- 한영 정합 — 핵심 메시지, H2 구조 대응, 데이터 일치, hreflang 상호 참조, slug 일치.
판정이 FAIL이면 발행 전에 멈춥니다. NEEDS_REVIEW면 Phase 2 산출물을 고쳐 다시 Phase 3을 돕니다. PASS여야 다음 게이트로 넘어갑니다.
품질 보고서는 한글본 글자 수와 영문본 단어 수를 brief의 mode 기준(long 1000012000자, short 50007000자)과 대조하고, 출처 인용 수와 H2 섹션 수를 같이 적습니다. 길이 기준이 AI_AUTOMATION.md의 Phase 2 length policy 한 곳에서만 정의되기 때문에, 보고서가 그 값을 다시 정하지 않고 참조합니다.
Phase 4 — ai-pattern-check 점수
섹션 제목: “Phase 4 — ai-pattern-check 점수”ai-pattern-check는 _workspace/posts/new/의 한글본과 영문본을 스캔해 0~100점을 매깁니다. 점수가 높을수록 AI 티가 짙습니다. 대부분의 카테고리는 Grep 패턴으로 자동 카운팅되고, 구조 균질성처럼 수동 판독이 필요한 자리는 따로 봅니다.
| 판정 | 점수 | 동작 |
|---|---|---|
| 합격 | 30 미만 | blog-publish로 진행 |
| 경고 | 30 ~ 60 | 명백한 자리를 먼저 고치고, 전체 자동화를 요청받은 경우에만 진행하며 사유 기록 |
| 불합격 | 60 초과 | 발행 차단. production-agent에 리라이트 요청, 최대 2회. 그래도 실패하면 사용자에게 핸드백 |
카테고리가 잡아내는 자리는 이런 것들입니다. 템플릿 표현(“정리하면”, “핵심은”, “독자 여러분”), 나열 패턴(“첫째/둘째/셋째”), AI 주어 과다(“AI에게 물었다”가 한 글에 여러 번), 영문 정형구(“In summary”, “It’s important to”), 결말의 다음 편 예고, 구조 균질성(모든 H2가 비슷한 길이, 모든 문단이 3~5줄), 결정성 연결어(“결국”, “즉”, “다만”의 집중), 결론형 정형구(“~에 있다”로 끝나는 문장 반복). 각 발견에 가산점이 붙고, 합이 임계값을 넘으면 판정이 떨어집니다. 정확한 패턴과 점수는 ai-pattern-check 스킬에 박혀 있습니다.
점수 임계값은 blog-orchestrator와 AI_AUTOMATION.md가 같은 값을 씁니다. 한쪽에서 임의로 다시 정의하지 않습니다.
게이트가 코드 레벨에서 강제되는 자리
섹션 제목: “게이트가 코드 레벨에서 강제되는 자리”불합격 글의 발행 차단은 오케스트레이터의 약속이 아니라 스크립트의 동작입니다. scripts/deploy_to_blogger.py가 업로드 직전에 AI-smell과 품질 게이트를 다시 확인하고, 불합격이면 업로드를 거부합니다. 오케스트레이터를 우회해 스크립트를 직접 불러도 막히는 이유가 여기 있습니다. --skip-gate는 재발행이나 긴급 상황을 위한 비상구로만 남겨 둔 자리입니다.
보안 기준선 S1~S8
섹션 제목: “보안 기준선 S1~S8”AI_AUTOMATION.md에 박힌 여덟 줄입니다. 강제 위치가 각각 다릅니다.
| 코드 | 규칙 | 강제 위치 |
|---|---|---|
| S1 | Google OAuth credentials.json/token.json, API 키는 git 추적 안 함 | .gitignore |
| S2 | _workspace/ 바깥 파일 수정과 삭제 금지. 인프라는 사람 승인 후 | 에이전트 정의 |
| S3 | 산출물 덮어쓰기 전 백업. 재발행 시 이전 버전 보존 | blog-publish |
| S4 | 외부 검색(research)은 사용자 명시 요청 시에만 | research |
| S5 | AdSense 정책 위반 가능성 글은 사전 검수 통과 후에만 발행 | adsense-optimization |
| S6 | 실명, 전화번호, 실주소 자동 검출 후 마스킹 권고. 자동 마스킹은 안 함 | quality-check |
| S7 | API 키와 OAuth 토큰은 본문, 로그, 산출물에 평문으로 박지 않음 | 모든 에이전트 |
| S8 | AI-smell 게이트 불합격 글은 Blogger 업로드 차단 | scripts/deploy_to_blogger.py |
S1과 S7은 자격증명을 다루는 자리고, S5와 S8은 발행 게이트입니다. S6은 자동으로 마스킹하지 않고 사람 결정에 맡기는 자리라, 실명이 들어간 글을 발행할지는 사용자가 정합니다.
다음 섹션은 publishing입니다. Blogger OAuth 자격증명을 어디에 두는지, scripts/deploy.sh의 staging과 dry-run과 발행 명령이 어떻게 갈리는지, 자격증명이 없을 때 어디서 멈추는지를 봅니다.