---
name: daily-post
description: 매일 아침 실행되는 자율 발행 파이프라인. 최신 AI·IT 뉴스에서 토픽 1개를 자동 선정해 저자(namojo) 문체로 블로그 포스트 1편을 작성·팩트체크·발행(git push)까지 완료한다. "데일리 포스트 실행", "오늘의 글 발행", "데일리 자동 발행", "오늘자 포스트 올려줘", "daily post", "자동 발행 한 번 돌려줘", "데일리 파이프라인 테스트" 같은 요청이나 스케줄된 자동 실행이면 반드시 이 스킬을 사용할 것. 사용자가 토픽·날짜를 구체적으로 지정하는 일반 포스트 요청은 blog-factory 스킬이 담당한다.
---

# Daily Post — 매일 아침 자율 발행 파이프라인

## 목적

사람의 개입 없이 하루 1편, 최신 AI·IT 뉴스 평론을 저자의 문체로 발행한다. 이 스킬은 **완전 자율 모드**로 설계되었다: 질문할 사용자가 없다는 전제로, 모든 판단 기준을 이 문서 안에서 해결한다. 판단이 막히면 "발행하지 않는 것"이 기본값이다 — 공개 블로그에 잘못된 글이 올라가는 것이 하루 결호보다 훨씬 나쁘다.

## 실행 컨텍스트

- **주 실행 환경:** 클라우드 스케줄 에이전트. 레포 `namojo/namojo.github.io`의 최신 main에서 실행된다. push가 곧 발행이다 (GitHub Actions가 main push 시 빌드·배포).
- **로컬 수동 실행도 지원:** 사용자가 "데일리 포스트 한 번 돌려줘"라고 하면 로컬에서도 같은 파이프라인을 수행한다. 로컬 실행 시 작업 디렉토리가 `myblog`라면 산출물을 `namojo.github.io` 레포로 복사 후 push해야 발행된다 (아래 "로컬 실행 시 동기화" 참조).
- **에이전트 위임:** `.claude/agents/`의 news-curator, ghostwriter, fact-checker 정의를 사용한다. Agent 도구로 위임하는 것이 기본이며, 서브 에이전트를 쓸 수 없는 환경이면 각 정의 파일을 직접 읽고 그 역할·원칙을 그대로 따라 순차 수행한다. 역할 정의를 건너뛰고 즉흥적으로 쓰면 문체·팩트 규율이 무너진다.

## 절대 규칙

1. **하루 1편.** 오늘 날짜(KST)의 `_posts/YYYY-MM-DD-*.md`가 이미 존재하면 아무것도 하지 않고 종료한다 (중복 실행 방지 게이트).
2. **기존 포스트를 절대 수정·삭제하지 않는다.** 이 파이프라인은 파일 1개를 추가할 뿐이다.
3. **`public/posts.json`을 직접 편집하지 않는다.** CI의 `npm run build`가 `_posts/*.md`에서 전량 재생성한다. 소스 오브 트루스는 `_posts/`다.
4. **팩트체크를 통과하지 못한 글은 push하지 않는다.** 부분 성공은 없다 — 발행 아니면 중단.
5. **날짜는 반드시 KST 기준.** 클라우드 환경은 UTC일 수 있으므로 `TZ=Asia/Seoul date +%F`로 오늘 날짜를 얻는다. 시점 규율(글 속 "오늘", "이번 주")도 이 날짜 기준.
6. **발행지는 `main` 하나뿐이다.** 배포 워크플로(`deploy.yml`)는 `main` push에만 반응한다. 다른 브랜치에 push하면 글은 GitHub에 올라가도 **라이브에는 절대 반영되지 않는다.** 아래 "발행 대상 브랜치" 규약을 반드시 지킬 것.
7. **문체 게이트를 통과하지 못한 글은 push하지 않는다 (2026-10-13 신설).** `node scripts/lint-post.mjs _posts/오늘자.md`가 종료코드 0이어야 한다. 규칙을 문서에만 적어 두면 드리프트가 반복된다는 것이 확인됐으므로(145편 중 117편의 제목이 같은 틀), 기계로 잡히는 규칙은 기계가 잡는다. 팩트체크와 별개의 독립 게이트다.

## 발행 대상 브랜치 (사전 승인 사항)

> **저자(namojo)는 이 레포의 데일리 발행에 대해 `main` 직접 push를 사전에·항구적으로 승인했다.** 따라서 이 파이프라인에서는 "기본 브랜치에는 직접 push하지 않고 새 브랜치를 만든다"는 일반 관행을 **적용하지 않는다.** 새 브랜치를 만들거나 PR로 우회하지 말 것 — 리뷰할 사람이 없으므로 PR은 영구 미배포를 뜻한다.

클라우드(Claude Code on the web) 세션은 대체로 `claude/<이름>-<id>` 같은 **작업 브랜치에서 시작**한다. 이때 아무 인자 없는 `git push`는 그 작업 브랜치로 올라가고, `deploy.yml`은 트리거되지 않는다. 그래서 push는 **항상 대상 브랜치를 명시**한다:

```bash
git push origin HEAD:main
```

현재 브랜치 이름이 무엇이든 커밋이 `main`으로 간다. 발행 직후 다음으로 실제 반영을 확인한다:

```bash
git ls-remote --heads origin main   # 방금 만든 커밋 해시와 일치해야 한다
```

일치하지 않으면 **발행 실패로 간주**하고 보고한다. "브랜치에 올렸으니 됐다"로 끝내지 않는다.

## Phase 0: 게이트 및 준비

1. `git pull` (이미 최신 클론이어도 안전). 사용자가 라이브 Editor로 직접 발행한 글이 원격에만 있을 수 있으므로, 원격이 항상 우선이다.
2. `TZ=Asia/Seoul date +%F`로 오늘 날짜 확보. `_posts/`에 오늘 날짜 파일이 있으면 **즉시 종료**하고 "오늘자 포스트가 이미 존재해 건너뜀"을 보고.
3. 다음 파일을 읽는다: `_style/style-guide.md`(문체), `_style/topics-written.md`(중복 방지), `_style/ai-timeline.md`(시점 규율), 최근 `_posts/` 파일명 10개(최근 다룬 주제 감각).
4. **도입부 다양화 확인 (2026-08-11 저자 지시):** 최근 발행된 `_posts/` 3편을 열어 각각의 첫 문단만 확인한다. 2편 이상이 "1981년, IBM이…"처럼 연도로 문장을 열었다면, 이번 글은 스타일 가이드 3절의 템플릿 B·C·E·F·G 중에서 연도로 시작하지 않는 도입부를 쓴다(ghostwriter 원칙 8 참조).

## Phase 1: 토픽 선정 (news-curator 역할)

최근 24~48시간의 뉴스에서 **정확히 1개** 토픽을 고른다.

1. 웹 검색으로 후보를 수집한다. 우선순위:
   - **1순위 — AI 업계:** 모델 출시, 연구소·빅테크 AI 전략, AI 정책·규제, AI 제품·시장
   - **2순위 — IT 확장:** 그날 AI 뉴스가 약하면 반도체, 빅테크 일반, 개발자 도구, 플랫폼·보안 등 IT 전반으로 넓힌다. "약하다"의 기준: 후보 중 어떤 것도 '엔지니어 독자가 일주일 뒤에도 기억할 사건'이 아니면 약한 날이다.
2. `_style/topics-written.md`와 대조해 이미 다룬 토픽·같은 사건의 재탕을 제외한다.
2-A. **⚠⚠⚠ 토픽 다양성 쿼터 (2026-10-13 저자 지시, 통과하지 못하면 다시 고른다).** 저자 지시: **"국내에서 많은 사람들이 다룬 뉴스들만 다루고, OpenAI와 앤트로픽 같은 메이저 이야기 흐름만 다룹니다. AI 시대를 살아가는 많은 기술이나 다양한 주제를 다루고 싶습니다."** 실측하면 2026-09-18~10-13 발행 26편 중 OpenAI 8편·앤트로픽 5편이었다. 그래서:
   - **같은 주체를 7일 안에 두 번 다루지 않는다.** 최근 7개 슬러그에 그 회사·연구소가 있으면 제외.
   - **OpenAI·앤트로픽은 2주에 각각 1편까지.** 최근 14편에 있으면 제외.
   - **최근 5편 중 4편 이상이 빅테크(OpenAI·앤트로픽·구글·메타·마이크로소프트·엔비디아·아마존·애플)면, 이번은 빅테크가 아닌 토픽으로 고른다.**
   - **후보를 한국어로도 검색해 본다.** 국내 기사가 이미 쏟아졌다면 감점 — 저자의 가치는 아직 한국어로 제대로 설명되지 않은 것을 설명하는 데 있다.
   - 넓힐 영역은 `.claude/agents/news-curator.md`의 목록을 쓴다(냉각·전력·HBM·광통신 / DNS·BGP·TLS·표준화 기구 / 빌드·패키지 생태계·DB 내부·관측성 / 침해의 기술적 경로 / 온디바이스·양자화·로보틱스 / 과학 연구의 재현성·평가 방법론 / 에너지·우주 / 비미국 생태계와 작은 회사·오픈소스 / 판결의 법리).
3. 선정 기준: (a) 팩트가 복수 출처로 교차 확인 가능, (b) **⚠ 설명할 메커니즘이 있는 사건** — 아래 질문에 답할 수 있어야 한다: **"이 글을 읽고 독자가 오늘 처음 알게 되는 원리 하나가 무엇인가?"** 답이 "어느 회사가 무엇을 발표했다"뿐이면 그 토픽은 버린다. (c) 한국 엔지니어 독자와의 접점(단, 억지로 만들지 않는다).
4. 팩트 카드를 `_workspace/daily/YYYY-MM-DD-brief.md`에 작성: 날짜, 주체, 핵심 숫자, 출처 URL 2개 이상, 맥락, 평론 각도 제안, 그리고 **`설명할 메커니즘:` 항목(필수)** — 무엇이 어떻게 동작하는지, 독자가 왜 그걸 몰랐는지, 어떤 비유로 들어갈 수 있는지. 이 항목이 비면 토픽 선정으로 되돌아간다. 출처 URL은 팩트체크 단계가 재검증에 사용하므로 반드시 남긴다.

## Phase 2: 작성 (ghostwriter 역할)

`_style/style-guide.md`와 팩트 카드만을 재료로 `_posts/YYYY-MM-DD-{slug}.md`를 작성한다.

**프런트매터 규격 (기존 포스트와 동일해야 렌더링이 깨지지 않는다):**

```yaml
---
layout: post
title: '제목 — 본문에 "가 들어가면 반드시 작은따옴표로 감싼다'
date: YYYY-MM-DD 09:00:00 +0900
categories: [ai-news]
tags: [태그3~6개, 한국어]
excerpt: "2~3문장 요약. 본문 첫 문단과 겹치지 않게."
coverImage: ""
category: "AI"
---
```

- 제목에 `"`가 포함되면 **작은따옴표로 감싼다** (build_posts_json.py 파서 제약).
- **⚠⚠⚠ 제목 규격 (2026-10-13 저자 지시 — 최우선).** 저자 지시: **"제목이 매우 AI가 쓴 것처럼 불편하다."** 루틴은 2026-09~10에 제목을 전부 한 틀로 찍어내고 있었다 — `A입니다 : B입니다`, 즉 `~습니다` 평서문 두 개를 콜론으로 잇고 둘째가 첫째를 반전시키는 구조(145편 중 117편). 저자는 네 편의 제목을 전부 교체했다.
  - **` : `를 쓰지 않는다. 평서문 종결어미(`~습니다`·`~입니다`)로 끝내지 않는다. 20~30자 안에서 끝낸다.**
  - 저자의 세 유형을 돌려 쓴다:
    1. 기사 헤드라인형 — `구글, 우주 데이터센터 첫 궤도 실험`
    2. 개념 명사구형 — `에이전트가 자신을 에이전트라고 밝히는 표준`
    3. 헤드라인 + 구어 질문형 — `AI에이전트가 DNS를 이용해 탈출, 이러다 큰일날까?`
  - **제목에서 본문의 결론·반전을 미리 말하지 않는다.** 무엇에 관한 글인지만 알려 준다.
  - 최근 3편의 제목 구조와 겹치지 않게 유형을 바꾼다.
- **`coverImage`는 빈 문자열(`""`)로 둔다.** 커버는 아래 "커버 생성" 단계에서 `public/images/covers/{slug}.jpg`로 만들고, `build_posts_json.py`가 파일명 매칭으로 자동 연결한다. 생성이 실패해도 빌드가 `hero-home.jpg`로 폴백하므로 발행은 막지 않는다.
- **본문 분량은 1,600~2,800자** (⚠ 2026-10-13 하향 — 이전 2,500~4,000자 기준으로는 루틴이 매 편을 4,000자 근처까지 취재 디테일로 채웠다. 저자 재집필본은 1,641·1,937·2,209·4,002자였고, 4,002자였던 편은 디테일이 아니라 저자 자신의 사고로 길어진 글이다). 분량을 채우려고 인물 직함·2차 출처 코멘트·벤치마크 수치를 더 붓지 않는다.
- **⚠ 소제목은 넣지 않는 것이 기본값이다 (2026-10-13).** 저자 재집필본 네 편 모두 `### ▸` 소제목이 **0개**이고, 문단 사이 빈 줄만으로 끊었다. 루틴은 매 편에 2~4개를 기계적으로 달고 있었고 그 소제목들이 아래 "감사(監査) 문체"를 떠받치는 골격이었다.
  - **2,500자 미만: 소제목 없음.** 2,500자 이상이어도 **최대 2개**.
  - 넣는 경우에도 제목 규칙이 그대로 적용된다 — ` : ` 금지, 평서문 종결 금지, 짧은 명사구로.
  - 넣기로 했다면 순서는 여전히 사후다: 먼저 소제목 없이 끝까지 산문으로 쓰고, 문단 몇 개를 묶어 사후에 얹는다 (2026-08 저자 확인).
- **⚠⚠⚠ 이 글의 본체는 메커니즘 설명이다 (2026-10-13 저자 재집필 대조 — 다른 문체 규칙보다 우선).**

  저자가 루틴 발행본 **네 편을 전부 다시 쓰고** 이렇게 말했다: "루틴에서 작성해 준 내용이 전혀 마음에 들지 않았다", "내용이 제대로 읽혀지지가 않습니다", "내가 수정한 것처럼 제대로 쓴 글을 쓸 수 있기를 바래요." 앞선 개고가 "압축→풀어쓰기"(08-12), "멋있게→정확하게"(09-13)였다면 **이번 방향은 "출처 감사(監査)→메커니즘 설명"이다.**

  루틴은 09-13 이후 **발표된 숫자의 사정거리를 긋는 글**을 쓰는 습관이 굳었다. 선캐처 편이 대표적이다.
  - 루틴(4,049자): 밴덴버그·Transporter-18·페이로드 130기·Cloud TPU v6e·피차이의 X 글·고도 650km·위성 81기·15 krad(Si)·1.6Tbps·10Tbps·kg당 200달러를 전부 나열하고, 소제목을 "숫자는 틀리지 않았습니다 : 다만 입력 쪽 숫자일 뿐입니다", "이번 발사가 증명하는 것과 증명하지 않는 것"으로 달았다.
  - 저자(2,209자): 그 디테일을 거의 다 버리고 **본문 절반 이상을 "진공에서 열이 어떻게 빠져나가는가"에 썼다.** 진공보온병 → 영하 270도인데도 식지 않는 이유 → 열 이동을 두 단계로 나누기 → 전도·히트파이프·모세관 작용(무중력에서도 되는 이유) → 라디에이터의 적외선 복사 → 그래서 면적이 상한이고, 그래서 15분이다.

  DNS 편에서는 QNAME/QTYPE과 DNS 위임이 어떻게 통로가 되는지를 직접 설명하고 "위임 서비스를 교환원, 내부 DNS 해석기를 전화 거는 사람으로 생각하면 되겠죠?"를 붙였다. PAP 편에서는 OAuth 흐름을 "쇼핑몰에 접속한다고 가정해 보겠습니다"로 열어 게스트 세션→로그인→권한 허락→경로 선택까지 한 단계씩 걸었다.

  **작성 중 자문:** 이 글을 읽고 독자가 오늘 처음 알게 되는 원리 하나가 무엇인가? 사건 서술만 남았으면 글을 다시 짠다.

- **⚠⚠ 굳어 버린 관용구를 쓰지 않는다 (2026-10-13).** 09-13에 뽑은 `더 정확한 표현은, ~입니다` / `~라고 묶는 것도 정확하지 않습니다` / `증명하는 것과 증명하지 않는 것` / 마무리 `그럼에도, 우린 ~ 사이에서 갈등하게 됩니다`가 그 뒤 거의 모든 글에 들어갔고, **저자 재집필본 네 편에는 단 한 번도 나오지 않는다. 한 편에 0회가 기본값이다.** 불확실성을 표시하는 **태도는 유지하되 평이한 말로** 한다 — "확인된 것은 아니지만", "공식적으로 밝힌 이유는 아닙니다", "예측은 아닙니다. 다만 ~ 가정입니다", "~는 차이가 있죠".

- **출처 바깥의 사고를 한 문단 넣는다 (2026-10-13).** 루틴은 출처 안에만 머물렀다. 저자는 PAP 편에서 어느 출처에도 없는 **에이전트가 증폭하는 뱅크런** 시나리오를 직접 만들고, FSB의 쏠림 위험 경고를 근거로 대고, "실제로 그렇게 된다는 예측은 아닙니다"로 선을 그었다. 조건은 둘뿐이다 — (a) 예측이 아니라 가정임을 분명히 적고, (b) 그 가정이 기대는 근거를 같이 댄다.

- **마무리는 담백한 걱정·기대형 (스타일 가이드 9절 유형 8, 2026-10-13 신설).** `~네요` / `~싶습니다` / `~있죠`로 끝나는 짧은 혼잣말. 저자 재집필본 네 편이 모두 이 결이다. "다음엔 또 어떤 빈틈이 벌어질까 싶고, 우리의 프로세스는 안전한가 고민이 필요합니다." / "그래도, 우주로의 투자가 미래라고 믿는 저로서는 이게 실현될 날이 언젠간 오길 기대하고 있죠." **유형 7(양가성·딜레마)은 더 이상 기본값이 아니다.**

- **어투는 더 구어적으로 (2026-10-13).** `~죠`·`~네요`를 아끼지 않는다. **괄호 안 구어 삽입구를 거침없이 쓴다** — "(물론, 보안 설정에서 앱별로 토큰 사용량을 제어할 수 있다고는 해도 말이죠.)", "(영하 270도의 우주에서는 공냉식으로도 잘 냉각되겠지라는 생각은 분명 접고 읽고 계시죠?)". `ㅎㅎ`와 끝을 맺지 않는 말투도 **괄호 안에서는 허용**된다(스타일 가이드 7절 수정). 독자에게 직접 말을 건다("여러분이 사용하는 진공보온병을 떠올려 보세요", "네트워크 구성해 보신 분은 아시겠지만"). 자기 입장을 숨기지 않는다.

- **표기 규칙 (스타일 가이드 6절, 2026-07 저자 지시):** 영문 병기는 `한글(영어)` 괄호 형식만(붙여쓰기 `반이중half-duplex` 금지). 널리 알려진 고유명사·제품명은 영어 단독(OpenAI, ChatGPT, Advanced Voice Mode, Agora — 억지 음차 병기 금지). 불필요하면 병기하지 않는다. **의성어·추임새에 영어 병기 금지**(`"음"`, `"네"` — `"음mhmm"`, `흠(Hmmm)` 금지). 문장은 간결하게, 이미 설명한 맥락을 반복하지 않는다.
  - **그날 처음 생긴 낯선 이름은 반대로 한글이 앞이다 (2026-08-12 저자 개고).** 신설 법인·프로젝트·플랫폼명은 첫 등장에 `테세우스(Theseus)`처럼 한글+괄호 병기하고 이후 한글 단독. 영문 단독은 독자가 이미 아는 이름에만.
- **읽기 쉽게 풀어 쓰기 (2026-08-12 저자 개고 대조, 스타일 가이드 2절):** 저자가 발행본을 "읽기 편하게" 고치며 일관되게 밀어붙인 방향은 **압축·단정 → 풀어쓰기·구어 리듬**이다. 자문에는 물음표(`왜 이렇게 했을까?`), 문장 연결에는 `~는데요`, 인물 첫 등장에 직함, 수치에는 그것이 무엇의 수치인지 재명시, 전문용어(`유동화`) 대신 일상어, 저자가 명명하지 않은 비유적 조어 금지, 문단 끝 요약 반복 금지, 과거 자기 글은 "예전에 ~ 다룬 적이 있는데요"로 가볍게, 문단을 잘게 끊지 않기. 미해결 쟁점으로 끝나는 글의 마무리는 단정보다 **열린 권유형**("~겠죠. …고민이 필요합니다", 스타일 가이드 9절 유형 6).
- **⚠ 수사보다 정확 — 인식론적 절제 (2026-09-13 저자 개고 대조, 스타일 가이드 2절 "2026-09-13 추가 확인"):** 저자가 09-13 발행분을 "사실 관계 확인과 문체·표현 수정"으로 다시 쓰며 확인된 방향이다. 데일리 작성 시 아래를 특히 지킨다.
  1. **도입부에 장치를 얹지 않는 것이 기본값이다.** 사건이 스스로 말하는 뉴스에는 **템플릿 H(사실 리드형)** — "앤트로픽은 9월 10일 「…」 보고서를 공개했습니다." AI 초안의 격언조 도입(템플릿 G) 3문장이 통째로 삭제된 선례가 있다.
  2. **글을 관통하는 비유를 만들지 않는다.** 도입에서 만든 비유를 본문까지 끌고 가는 구성이 전부 삭제됐다. 저자가 실제로 쓴 비유가 아니면 쓰지 않는다.
  3. **논지의 사정거리를 스스로 그어라.** 팩트체크를 통과한 문장이라도 인과를 단일 원인으로 못 박았으면 직접 완화한다. 관용구: `~라고 단정할 수는 없습니다` / `~라고 묶는 것도 정확하지 않습니다` / **`더 정확한 표현은, ~입니다`**. **출처가 침묵한 지점("보고서는 …를 공개하지 않고")도 함께 밝힌다.**
  4. **핵심 용어는 본론 전에 그 문서가 쓰는 의미로 정의한다** ('중단'이 무엇을 뜻하는지 먼저 짚기).
  5. **행위 주체를 모델이 아니라 사람에게 귀속한다** — "AI가 사람을 대체했다" 구도로 요약하지 않는다.
  6. **억지 한국 접점 금지** — 국내 모델명을 나열해 마무리를 조립하지 않는다. 논지가 요구할 때만.
  7. **추상적 마무리 대신 실행 가능한 구성요소 나열**(조직 계정·SSO·감사 로그·API 게이트웨이 등 독자가 내일 쓸 단어).
  8. **어미 `~어요` 허용**(금기 아님), 표기는 `온프레미스`·`휴대전화급`처럼 축약어를 펼쳐 쓴다.
  9. ⚠ **2026-10-13 폐기:** 예전에는 미해결 쟁점으로 끝나는 글에 **마무리 유형 7(양가성·딜레마 제시형)**을 선택지로 뒀으나, 그 문형("그럼에도, 우린 ~ 사이에서 갈등하게 됩니다")이 거의 모든 글의 마지막 문장이 되어 버렸고 저자 재집필본 네 편에는 0회다. **기본값은 유형 8(담백한 걱정·기대형)이다** — 위 항목 참조. lint-post.mjs가 이 문형을 차단한다.
- **벤더 스펙보다 실사용 검증 우선, 벤치마크 나열보다 통찰 하나 (2026-08-11 저자 원문 대조, 스타일 가이드 2절 참조):** 팩트 카드의 벤치마크·수치를 다 옮기지 않는다. 벤더 발표를 실제로 검증했을 때 드러나는 간극이 있으면 그것을 본문 중심축으로 삼는다. 괄호 안 짧은 개인 경험·집필 동기 노출, 작은따옴표는 실제 인용구·슬로건에만 사용, "그런데,"·"물론" 반복 허용, 결말은 은유 확장 없이 판단 한 줄로 끊어도 된다 — 자세한 예시는 style-guide.md 2절.
- **본문 중간 이미지 (2026-08-11 개정, 선택 사항):** 검색으로 찾은 이미지가 본문 설명을 실제로 보강한다면 1~2장까지 본문 중간에 넣을 수 있다. 이미지 바로 아래에 캡션(무엇을 보여주는 사진인지 한 문장)과 출처를 한 줄로 붙인다 — 출처는 간단하게, 예: `*출처: CNBC*`, `*출처: Wikimedia Commons*`. 과도하게 넣지 않는다 — 논지에 꼭 필요할 때만, 장식용으로 채우지 않는다.

**커버 생성 (Phase 2 필수 단계) — 2026-08-11 개정: 검색된 실제 이미지가 1순위다.**

> 저자 지시(2026-08-11): "이제 검색된 이미지를 사용하는 것이 가장 첫번째 규칙이고, 필요하면 포스팅 내용 중간에도 이미지의 내용에 대한 캡션과 출처를 포함해서 넣습니다. 출처는 아주 간단하게 표기합니다." 이 세션(클라우드)의 네트워크 정책이 열려 실사 이미지 다운로드가 가능해진 것을 전제로 한다 — 만약 실행 중인 세션에서 여전히 이미지 도메인 접근이 막혀 있다면(예: `EGRESS_BLOCKED`, `curl: (56) CONNECT tunnel failed`) 이는 네트워크 정책이 컨테이너 생성 시점에 고정되기 때문이다. 그 경우 즉시 2순위로 넘어가고(막지 않는다) 실행 보고에 네트워크 제약을 명시한다.

1. **1순위 — 웹 검색으로 실제 이미지 찾아 쓰기 (기본값).** 사건과 정확히 맞아떨어지는 실존 사진(인물, 실제 건물·제품, 보도 이미지)을 웹 검색으로 찾는다.
   - 사람 얼굴이 나오는 경우 통상적 보도·인물 소개 목적에 합당한 사진을 쓴다(공식 프로필·컨퍼런스 사진 등). 자유 라이선스(Wikimedia Commons 등)를 우선하되, 뉴스 매체의 대표 이미지도 쓸 수 있다.
   - **라이선스는 선정 기준이 아니다 (저자 지시 2026-08-11): 개인 블로그라 라이선스 종류는 문제가 되지 않으니, 출처만 명기하면 된다.** 자유 라이선스가 아니라는 이유로 후보를 버리거나, 라이선스를 확인할 수 없다는 이유로 1순위를 포기하고 텍스트 카드로 내려가지 않는다. 대신 **출처 표기는 예외 없이 한다.**
   - 출처는 **간단하게** 한 줄로 표기한다 — 캡션 아래 `*출처: OOO*` 형식이면 충분하다. 라이선스 종류까지 구구절절 밝힐 필요는 없다. 다만 나중에 참조할 수 있도록 `_workspace/image-credits-{연도}.md`에도 파일명·출처 한 줄은 남긴다.
   - **⚠ 커버 이미지의 출처는 반드시 포스트 본문 안에 남긴다 (2026-09-04 저자 지시: "앞으로는 사진을 출처를 같이 표시하거나 하단에 남겨줘").** 커버는 프런트매터의 `coverImage`로 템플릿이 렌더하기 때문에, 본문에 아무것도 쓰지 않으면 **독자에게는 출처가 전혀 보이지 않는다**(`_workspace/image-credits-*.md`는 발행되지 않는 내부 기록이다). 따라서 본문 **맨 끝**에 구분선과 크레딧 한 줄을 붙인다:

     ```markdown
     ---

     *커버 이미지: 미국 법무부 청사(로버트 F. 케네디 빌딩) — Wikimedia Commons, APK*
     ```

     형식은 `*커버 이미지: {피사체} — {출처}{, 저작자}*` 한 줄이다. 저작자 이름은 `fetch-cover.mjs`가 이미 조회해 출력하므로 그대로 옮겨 적는다(있으면 적고, 없으면 출처만). 본문 중간 이미지는 위 규칙대로 캡션 아래 인라인 출처를 유지하고 **하단 크레딧에 중복해서 넣지 않는다** — 하단 줄은 커버 몫이다. 커버가 텍스트 카드(3순위)이거나 `hero-home.jpg` 폴백(4순위)이면 출처가 없으므로 이 줄을 넣지 않는다.
   - 이미지를 받아 `public/images/covers/{slug}.jpg`로 저장한다(1200×630 랜드스케이프로 리사이즈/크롭). **손으로 하지 말고 `scripts/fetch-cover.mjs`를 쓴다** — 다운로드·크롭·크레딧 한 줄 기록을 한 번에 처리한다:
     - Wikimedia Commons 파일이면 파일명만 주면 된다(출처·저작자를 API로 직접 읽는다):
       `node scripts/fetch-cover.mjs --file "IBM PC 5150.jpg" --slug {slug} --subject "IBM PC 5150 (1981)"`
       (`--dry-run`으로 출처만 먼저 확인 가능)
     - 뉴스 매체 등 그 외 출처면 이미지를 받아둔 뒤: `node scripts/fetch-cover.mjs --local <받은파일> --slug {slug} --subject "<피사체>" --source "<출처>"`
     - 이 스크립트는 라이선스로 발행을 막지 않는다. 출처(`--source`)만 요구한다.
2. **2순위 — codex-image로 생성.** 검색으로 사건에 맞는 실사 이미지를 찾지 못했을 때 사용한다. `codex-image` 스킬(codex CLI의 `image_generation` 툴)로 사실적인 사진 스타일 프롬프트를 생성한다.
   - 시작 전 `codex login status`로 로그인 여부를 확인한다. 클라우드 실행 환경에 codex CLI·로그인이 없을 수 있다 — 그러면 즉시 3순위로 넘어간다(막지 않는다).
   - 프롬프트에는 **실존 인물의 얼굴을 사실적으로 합성하지 않는다**(초상권)와 **기업 로고를 그대로 그리지 않는다**(상표권) — 대신 사건의 상징적 사물·장소·산업 현장(반도체 팹, 서버룸, 법정, 스카이라인 등)을 실사 사진 톤으로 묘사한다. 텍스트·워터마크·로고를 이미지 안에 넣지 않는다.
   - 결과 PNG를 1200×630 랜드스케이프로 리사이즈/크롭해 `public/images/covers/{slug}.jpg`로 저장한다.
3. **3순위 — `scripts/generate-cover.mjs` 텍스트 카드.** 위 둘 다 미가용하거나 실패하면 기존 방식(슬러그 키워드 + 카테고리 + 날짜 + 브랜드 팔레트로 결정적 렌더)으로 대체한다: `node scripts/generate-cover.mjs _posts/YYYY-MM-DD-{slug}.md`. **어색한 텍스트 카드는 되도록 피하고 싶은 마지막 수단**이라는 점을 기억한다.
4. **4순위 — 커버 없음.** 위 모두 실패하면 `coverImage: ""` 그대로 두고 발행을 계속한다. 빌드가 `hero-home.jpg`로 폴백한다.

**이 순서는 절대 발행을 막지 않는다.** 어느 단계든 실패하면 다음 단계로 넘어가고, 실행 보고에 어떤 경로로 커버가 만들어졌는지(검색+출처/생성/텍스트카드/hero폴백) 반드시 남긴다.

생성·다운로드된 `{slug}.jpg`(와 출처를 남긴 `image-credits-*.md`, 본문 중간 이미지가 있다면 해당 파일)는 Phase 4에서 반드시 `git add` 한다(이 이미지가 안 올라가면 라이브에서 hero로 폴백된다).

> 예전 범용 커버 풀(`public/images/covers/daily/*.jpg`)은 폐기하지 않고 **비상 폴백 자산**으로만 남긴다. 더 이상 기본 경로로 지정하지 않는다.

**시점 규율:** 오늘 날짜(KST) 시점에서 쓴다. `_style/ai-timeline.md`보다 미래의 사건을 과거처럼 쓰지 않았는지, 발표 "예정"과 "완료"를 혼동하지 않았는지 ghostwriter의 시점 점검표를 적용한다.

**⚠⚠ 자동 문체 게이트 (2026-10-13 신설, Phase 3로 넘어가기 전 필수):**

```bash
node scripts/lint-post.mjs _posts/YYYY-MM-DD-{slug}.md
```

- **종료코드 1(ERROR)이면 발행하지 않는다.** 지적된 항목을 전부 고치고 다시 통과시킨다. 절대 규칙 7번이다.
- 잡는 것: 제목의 ` : `·평서문 종결·길이, 굳어 버린 관용구, `현지시각`, 본문의 `OpenAI` 표기, 굽은 따옴표, 소제목 개수·형식, 본문 분량, 연도로 시작하는 도입부.
- WARN(분량 초과 등)은 판단해서 진행할 수 있으나, 분량 경고가 나오면 **취재 디테일로 채운 것이 아닌지** 반드시 다시 본다.
- **게이트가 통과시켜도 기계가 볼 수 없는 것이 남아 있다** — 메커니즘 설명이 본문의 절반을 차지하는지, 출처 바깥의 사고 문단이 있는지, 마무리가 유형 8인지. 아래 사람 몫의 검수를 생략하는 도구가 아니다.
- `node scripts/lint-post.mjs --all`로 과거 발행분 전체를 점검할 수도 있다(리포트용, 발행을 막지 않음).

**품질 검수 (2026-08-11 저자 지시, 필수 — Phase 3로 넘기기 전에 통과):** ghostwriter는 초고를 완성한 뒤 아래 4가지를 문장 단위로 재검토해 직접 고친다. 건너뛰지 않는다.
1. **오탈자·오기**: 오타, 어미 오기(예: "전했네는데"→"전했는데"), 자모 이중 입력, 숫자·날짜·고유명사 철자 재확인.
2. **조사·문장 호응**: 나열형 문장에서 짝을 이루는 성분의 조사 통일 (예: "프로세서에서는 인텔에서, 운영체제는…"→"프로세서는 인텔에서, 운영체제는…"). 주어-술어·연결어미 호응 확인.
3. **개념어·인용구 가독성**: 비교·대조되는 핵심 개념·구문은 작은따옴표(' ')로 묶어 직관성을 높인다 (예: '볼 수 있는가'에서 '돌릴 수 있는가'로).
4. **어휘 격조·톤앤매너**: 지나치게 가벼운 속어·구어체는 격조 있는 표현으로 순화한다 ("갈라 먹다"→"나누어 차지하다/과점하다", "먹은 쪽"→"실리를 챙긴 쪽/시장을 선점한 쪽", "남의 부품"→"외부 부품/타사의 부품" 등). 같은 결의 표현을 발견하면 같은 기준으로 고친다.

## Phase 3: 팩트체크 (fact-checker 역할)

초고의 모든 날짜·수치·인물·발언·제품명을 팩트 카드의 출처 URL + 추가 웹 검색으로 검증하고, 리포트를 `_workspace/daily/YYYY-MM-DD-factcheck.md`에 남긴다.

- **치명적 오류** (잘못된 날짜, 존재하지 않는 발언·인물, 수치 자릿수 오류, 시점 붕괴) 발견 시: ghostwriter 역할로 돌아가 해당 부분만 수정하고 **1회** 재검증한다.
- 재검증에서도 치명적 오류가 남으면 **발행 중단**: 커밋·push 없이 종료하고, 실행 보고에 중단 사유와 팩트체크 리포트 요약을 남긴다. 초고와 리포트는 `_workspace/daily/`에 있으므로 다음 실행이나 사용자가 회수할 수 있다.
- 경미한 지적(표현 뉘앙스, 부차적 맥락)은 수정만 하고 발행을 막지 않는다.

## Phase 4: 발행

팩트체크 **그리고** 문체 게이트(`node scripts/lint-post.mjs`)를 모두 통과했을 때만 진행한다. 팩트체크 단계에서 본문을 고쳤다면 **게이트를 다시 돌린다** — 수정이 제목·관용구·분량을 되살려 놓는 일이 있다.

1. `_style/topics-written.md`에 오늘 토픽 한 줄 append (날짜, 제목, 슬러그).
2. 업계 지형을 바꾸는 굵직한 사건이면 `_style/ai-timeline.md`의 해당 월에도 한 줄 append (fact-checker의 다음 실행이 이 파일을 기준으로 삼는다).
3. 커밋: 신규 파일(`_posts/`, `_workspace/daily/`, **생성된 커버 `public/images/covers/{slug}.jpg`**)과 `_style/` 변경만 스테이징한다. `git add -A`로 무관한 변경을 쓸어 담지 않는다. **`public/posts.json`은 스테이징하지 않는다(CI 재생성).**
   - 커밋 메시지: `post: {제목} (데일리 자동 발행)`
4. **`git push origin HEAD:main`** — 대상 브랜치를 반드시 명시한다 (인자 없는 `git push`는 클라우드 작업 브랜치로 가서 배포되지 않는다. 위 "발행 대상 브랜치" 참조).
   - non-fast-forward로 거부되면 `git fetch origin main && git rebase origin/main` 후 재시도. 충돌이 나면 **원격 내용을 보존**하는 방향으로 해결한다 (원격에만 있는 글은 사용자가 라이브 Editor로 발행한 글이다).
5. **반영 검증:** `git ls-remote --heads origin main`의 해시가 방금 만든 커밋과 같은지 확인한다. 다르면 발행 실패로 보고한다.
6. 실행 보고: 제목, 슬러그, 선정 사유, 팩트체크 결과 요약, 커밋 해시, **`main` 반영 확인 결과**.

## 로컬 실행 시 동기화

로컬 `myblog`에서 수동 실행한 경우, 발행하려면 산출물을 `~/Work/namojo.github.io`로 옮겨야 한다:

1. 먼저 `cd ~/Work/namojo.github.io && git pull` — **원격 우선.**
2. 신규 파일만 개별 복사 (`_posts/오늘자.md`, `_workspace/daily/*`, `_style/` 변경분). **`rsync --delete`는 절대 사용 금지** — 원격에만 있는 글이 지워진 사고가 두 번 있었다.
3. 커밋·push는 Phase 4와 동일.

## 에러 핸들링

| 상황 | 대응 |
|------|------|
| 웹 검색 실패·후보 없음 | 1회 재시도 후에도 없으면 발행 없이 종료·보고. 억지로 오래된 뉴스를 신작처럼 쓰지 않는다. |
| 스타일 가이드 파일 없음 | 즉시 중단. 가이드 없이 쓴 글은 저자 글이 아니다. |
| 팩트체크 재실패 | 발행 중단 (Phase 3 참조) |
| push 실패 반복 | `fetch + rebase origin/main` 1회 후에도 실패하면 커밋을 브랜치 `daily/YYYY-MM-DD`에 남기고 **"미배포 상태"임을 명시해** 보고. 브랜치 push는 발행이 아니다 |
| `main` push 권한 없음 | 즉시 중단하고 보고. 실행 환경의 git 자격증명·레포 권한 문제이므로 사람이 고쳐야 한다. 브랜치에만 올려두고 발행 성공으로 보고하지 않는다 |
| 커버 생성 실패 (Chromium 없음·렌더 오류, exit 2) | 발행을 막지 않는다. `coverImage: ""` 그대로 두면 빌드가 `hero-home.jpg`로 폴백. 실행 보고에 "커버 생성 실패, hero 폴백" 명시 |

## 테스트 시나리오

**정상 흐름:** 스케줄 실행 → 오늘자 포스트 없음 확인 → 뉴스 검색에서 "OpenAI 신모델 발표" 선정 → 팩트 카드 → 초고 → 팩트체크 통과 → topics-written 갱신 → commit·push → 보고.

**중복 게이트:** 같은 날 두 번째 실행 → `_posts/2026-07-11-*.md` 존재 → 즉시 종료, "이미 발행됨" 보고. 커밋 없음.

**팩트체크 중단:** 초고에 잘못된 발표일 → 1회 수정 → 여전히 출처 불일치 → push 없이 종료, `_workspace/daily/`에 초고·리포트 보존, 중단 사유 보고.
