# 워녕이(Telegram Hermes Bot) 최근 3일 메시지 감사

- 범위: 2026-08-01 00:00 ~ 2026-08-03 11:10 KST
- 방식: cron output, execution ledger, 제품별 receipt, world designer queue/state, 발송 코드 대조
- 제외: bot token·비밀값·개인 포트폴리오 수치/본문
- 한계: Telegram Bot API에는 봇이 보낸 전체 대화 이력을 다시 읽는 기능이 없고, Hermes도 공통 outbound body/message_id 원장을 남기지 않는다. 따라서 실제 채팅의 완전한 사본이 아니라 로컬 증거로 재구성한 감사다.

## 결론

사용자가 본 세계설계자 반복은 정확한 URL 중복이라기보다 **알림 단위가 잘못 설계된 결과**다.

1. `세계 설계자 T1 수집`은 후보 문서 한 건마다 Telegram 한 건을 보낸다.
2. 8/3 실행에서 신규 URL 7개를 발견했고, 그중 6개가 앤드리슨이었다.
3. 여섯 URL은 서로 달라 URL dedupe는 작동했다.
4. 그러나 앤드리슨 저자 허브에서 95개 링크를 읽고 발행일·실제 저자·최신성 검증 없이 “처음 본 URL”을 신규로 간주한다.
5. 큐 124개가 전부 `candidate`이고 정식 소비/승인 전이가 없어 계속 쌓인다.

`세계 설계자 X 감시` 자체는 사용자 보고서가 아니다. 등록된 designer X 계정을 5개씩 묶어 최근 24시간 정책·투자·기술·거시 게시물을 관측 원장에 넣는 수집기다. 코드 계약상 직접 Telegram 발송은 0회다. 사용자가 본 같은 이름의 메시지는 8/1 자동 수리공이 7/23의 오래된 X API 429 사고를 8일 늦게 진단해 보낸 것이었다.

## 실제로 재구성되는 세계설계자 메시지

### 8/1 00:15

- 자동 수리공 직접 발송 1건: `자동 수리공 진단 — 세계 설계자 X 감시`
- 원 사고 시각: 7/23 20:30
- 진단 시각: 8/1 00:15
- 내용: 당시 외부 X/Grok rate-limit 사고를 EXTERNAL로 분류
- 문제: 이미 오래된 사고인지, 현재 작업이 회복됐는지 재확인하지 않고 발송

### 8/3 08:30

- 세계설계자 T1 수집 직접 발송 7건
- 구성: 앤드리슨 6건, 베센트 1건
- 결과: `new=7`, `notify_failed=0`
- 여섯 앤드리슨 후보의 URL/evidence_id는 모두 서로 다름

즉 최근 3일 로컬 증거상 세계설계자 관련 직접 발송은 최소 8건으로 재구성된다. 정확한 Telegram message_id는 저장되지 않았다.

## 왜 같은 이름이 연속되는가

### T-01 · HIGH — 후보 1건 = 메시지 1건 계약

- 실패 시나리오: 한 사람의 허브에서 URL 20개가 새로 보이면 같은 인물 이름으로 20개 메시지가 연속 발송된다.
- 파일: `scripts/world_designers_collect.py:8`, `:561-567`, `:413-418`
- 테스트가 이 동작을 의도적으로 고정한다: `scripts/test_world_designers_collect.py:217-243`은 7개 후보면 알림도 7개여야 한다고 단언한다.
- 판정: 우연한 버그가 아니라 현재 설계 자체다.

### T-02 · HIGH — URL 신규성과 콘텐츠 최신성을 혼동

- 실패 시나리오: 저자 페이지 렌더링이 바뀌어 예전 링크가 새로 노출되면 오래된 콘텐츠도 오늘의 신규 후보로 발송된다.
- 파일: `scripts/world_designers_collect.py:240-267`
- 앤드리슨 필터는 `/podcast/*`면 모두 문서로 허용하고, 단일 루트 URL도 제외목록에 없으면 허용한다.
- 발행일, 실제 저자, 페이지 게시 시각을 저장/검증하지 않는다. `discovered_at`은 게시일이 아니라 Hermes가 URL을 처음 본 시간이다.
- 8/3 실측: 앤드리슨 허브에서 95개를 문서 후보로 파싱했다.

### T-03 · HIGH — 큐가 끝나지 않음

- 상태: `state/world_designers_queue.jsonl` 124행, 전부 `status=candidate`.
- `world_designers_collect.py`에는 candidate→approved/rejected/consumed 전이가 없다.
- 별도 body fetcher는 본문 수집 여부만 바꾸며 후보 상태를 소비하지 않는다.
- 결과: 후보는 계속 누적되고 사용자는 무엇을 승인해야 하는지 알 수 없는 알림을 받는다.

### T-04 · MEDIUM — 자동 수리공이 오래된 사고를 뒤늦게 발송

- 파일: `scripts/auto_healer.py:97-114`
- `pop_incidents()`는 처리 여부와 ERROR 종류만 보고 TTL·현재 job 상태·회복 여부를 검사하지 않는다.
- `auto_healer.py:88-93`, `:290-291`에서 진단을 워녕이에게 직접 발송한다.
- 8/1에는 7/23 X 감시 사고와 7/24 dossier 사고를 각각 뒤늦게 진단했다.

### T-05 · MEDIUM — execution ledger의 `notify_status=sent`가 실제 Telegram 전송을 뜻하지 않음

- `hermes-agent/cron/scheduler.py:751-757`: `deliver=local`이고 target이 없으면 실제 발송 없이 성공(None)을 반환한다.
- `scheduler.py:2282-2305`: 내용이 있고 delivery error가 없으면 실제 target 유무와 무관하게 `sent`로 기록한다.
- 최근 3일 원장의 `sent=1,343`은 실제 워녕이 메시지 수로 사용할 수 없다.
- 반대로 T1 후보·자동 수리공처럼 스크립트가 직접 보낸 메시지는 execution ledger에 개별 건수/본문/message_id가 남지 않는다.

## 속보 최근 3일

cron 기준 생산 실행만 구분했다. 8/3 새벽 테스트가 생산 receipt에 섞어 넣은 synthetic 행은 제외했다.

| 날짜 | 예정 실행 | 제품 전달 성공 | 제품 미전달 | 판정 |
|---|---:|---:|---:|---|
| 8/1 | 8 | 7 | 1 send failure | 대체로 작동 |
| 8/2 | 8 | 3 | 5 LLM timeout | 심각한 열화 |
| 8/3 08시까지 | 3 | 0 | 3 | 현재 중단 상태 |

- 마지막 성공: 8/2 23:10
- 8/3 02시·05시: Oracle Claude 주간 한도 소진 → `delivery=not_attempted`
- 8/3 08시: LLM timeout → `delivery=not_attempted`
- 따라서 사용자가 말한 “현재 속보가 안 온다”는 맞다. 현재 원인은 수집 0이 아니라 생성/Oracle 단계 실패다.
- 실패 회차도 scheduler가 watchdog 오류 알림을 보낼 수 있어, 운영 알림과 실제 속보 제품 메시지가 섞여 보인다.

## 도시에 최근 3일

- schedule: `12 6 * * 2-6` = 화~토 06:12
- 8/1 토요일: 실행 성공, 파일 저장 및 full summary/body 전달 경로 성공 receipt 존재
- 8/2 일요일: 일정상 실행 없음
- 8/3 월요일: 일정상 실행 없음
- 따라서 최근 이틀의 무소식은 장애가 아니라 현재 cron 설계다. 사용자가 “하루 시장 복기”를 매일 기대한다면 schedule과 제품 기대가 불일치한다.
- dossier receipt는 Telegram send 함수가 bool만 반환해 message_id를 저장하지 못한다.
- 8/1 00:17 자동 수리공이 7/24의 오래된 dossier SIGSEGV 의심 사고를 발송했고, 같은 날 실제 dossier는 07:04 성공했다. 오래된 장애 알림이 현재 상태처럼 보여 혼란을 만들었다.

## “워녕이에게 보낸 메시지 전부” 확인 가능 여부

현재는 완전 확인이 불가능하다.

- 가능한 것: cron 실행 문서, 직접 발송 직전의 후보/진단 원문, 제품 receipt, 성공/실패 재구성
- 불가능한 것: 실제 Telegram에 도착한 모든 메시지의 완전한 순서·본문·message_id·삭제 여부
- 이유: direct notifier와 scheduler delivery를 함께 묶는 공통 outbound ledger가 없고, dossier도 message_id 미노출을 명시한다.

필요한 구조는 `outbound_messages.jsonl` 한 곳에 `ts/product/message_hash/message_id/delivery_status/dedupe_key`를 기록하는 것이다. 개인 본문은 저장하지 않고 hash와 요약 라벨만으로도 발송 수·중복·제품 상태를 정확히 감사할 수 있다.

## 수정 우선순위

1. P0: 세계설계자 후보를 후보별 발송에서 **실행당 1개 묶음 요약**으로 변경
2. P0: 발행일/실제 저자/최신성 검증 없는 후보는 Telegram 금지, 큐에만 저장
3. P0: auto-healer에 TTL과 현재 회복 상태 재검사 추가
4. P1: world designer queue consumer/승인/폐기 상태 전이 구현
5. P1: 실제 target·message_id 기반 outbound delivery ledger 도입
6. P1: 속보의 Claude Oracle을 독립 provider로 failover하고 제품 오류와 운영 오류 채널 분리
7. P2: dossier가 일~월에도 필요한지 제품 schedule을 사용자 기대와 재확정

## 테스트

```text
test_world_designers_collect.py + test_x_designer_watch.py + test_auto_healer.py
38 passed, 1 failed
```

실패는 기존 `test_cap_skip_not_marked_done`: Claude 보호 모드가 auto-healer main을 선제 skip해 cap 잔류 테스트가 3건 대신 5건으로 남는다. 세계설계자 URL dedupe 테스트는 통과하지만, 최신성·묶음 발송·consumer 완료 테스트는 없다.
