Crowny Today — "맥 = AI 워커" 아키텍처 구현
개요
today.crowny.org(VPS, 실서비스, 로컬 미러 127.0.0.1:9604 동일 소스)에 AI 없는 작업큐 API + 브리핑 캐시를 추가하고, 맥이 아웃바운드 폴링(LaunchAgent 300초)으로 큐를 소비해 셀코어/규칙+통계(토큰0) 브리핑을 생성, VPS에 POST하는 구조를 구현했다. VPS는 AI 실행을 전혀 하지 않는다 — 캐시 조회 + 큐 적재만.원격 VPS는 실재(cafe24, root@1.234.20.240, /opt/today, systemd today), /Users/ef/crowny-today/배포.sh가 배포 절차. 서버.한선 소스 1곳(맥)에서 컴파일 → rsync → 원격 재시작.
구현 파일
/Users/ef/crowny-today/서버.한선 — API 3종 추가 + 헬퍼(1230줄대→1300+줄)_처리AI브리핑 / _처리AI큐목록 / _처리AI결과 / _AI큐적재 / _AI큐대기존재 / _AI큐완료마킹 / _브리핑조회 / _브리핑저장 / _워커토큰검증 / _헤더값_인증확인에 워커토큰 Bearer 우회(admin 합성) 추가 — 워커가 /api/posts,/api/todos 통계 조회 시 사용/Users/ef/crowny-today/서버.toau — 재컴파일 산출물 (배포됨)/Users/ef/crowny-today/워커/브리핑생성.한선 — 4상(지금/마침/막힘/과부하) 규칙판정 + 문장조립, 자체시험 16개 전부 PASS/Users/ef/crowny-today/워커/브리핑생성.toau — 컴파일 산출물/Users/ef/crowny-today/워커/워커.sh — 헤드리스 폴링 워커(큐조회→통계수집→브리핑생성→선택적 haiku 다듬기→POST result)/Users/ef/crowny-today/워커/워커.conf — VPS_URL/WORKER_TOKEN (chmod 600)/Users/ef/CrownyBrowser/bundle/today/bridge/todayBridge.js — AI 브리핑 플로팅 카드(폴링 60초, null=생성 중/stale=흐리게) 추가/Users/ef/crowny-today/bridge.js — 위 파일 사본(cp 동기화, node --check 통과)~/Library/LaunchAgents/org.crowny.today-worker.plist — StartInterval 300, load 완료API
GET /api/ai/briefing?scope=team|user&user=NAME(세션 인증 필요) — 캐시 조회, 없으면 큐 자동적재(중복 스킵)GET /api/ai/queue(X-Worker-Token 헤더) — 대기 큐 목록POST /api/ai/result(X-Worker-Token 헤더) — {queueId,scope,user,text} → 브리핑.psv append + 큐 완료 마킹- PSV:
data/AI큐.psv(id|scope|user|status|ts),data/브리핑.psv(scope|user|text|ts) - 워커토큰:
ct-worker-9604-a17f3c2e(서버.한선·워커.conf 양쪽 동일값)
검증 증거 (실측, today.crowny.org 라이브)
1) 큐 적재 확인:
$ curl -s "https://today.crowny.org/api/ai/briefing?scope=team&user=전체" -H "Authorization: Bearer $TOKEN"
{"ok":true,"briefing":null}
$ curl -s -H "X-Worker-Token: ct-worker-9604-a17f3c2e" "https://today.crowny.org/api/ai/queue"
{"ok":true,"queue":[{"id":"aiq1784292010_2931","scope":"team","user":"전체","ts":1784292010}]}
2) 워커 실행 로그 (워커.sh, VPS_URL=https://today.crowny.org):
[2026-07-17 21:40:48] === 워커 시작 (VPS_URL=https://today.crowny.org) ===
[2026-07-17 21:40:48] 큐 응답: {"ok":true,"queue":[{"id":"aiq1784292010_2931","scope":"team","user":"전체","ts":1784292010}]}
[2026-07-17 21:40:48] 대기 큐 항목 수: 1
[2026-07-17 21:40:48] 처리 시작: id=aiq1784292010_2931 scope=team user=전체
[2026-07-17 21:40:48] 통계: {"scope": "team", "user": "전체", "postsToday": 10, "doneCount": 2, "planCount": 1, "todoTotal": 4, "todoDone": 1, "blockedCount": 0, "activeUsers": 7}
[2026-07-17 21:40:48] 생성된 브리핑(다듬기 전): 오늘 팀은 게시물 10건 중 2건을 마쳤고, 진행 중인 항목이 7건 있습니다. 할일은 1/4건 완료로 정상 범위입니다.
[2026-07-17 21:40:49] result POST 응답: {"ok":true}
[2026-07-17 21:40:49] === 워커 종료 ===
3) 브리핑 재조회:
$ curl -s "https://today.crowny.org/api/ai/briefing?scope=team&user=전체" -H "Authorization: Bearer $TOKEN"
{"ok":true,"briefing":{"text":"오늘 팀은 게시물 10건 중 2건을 마쳤고, 진행 중인 항목이 7건 있습니다. 할일은 1/4건 완료로 정상 범위입니다.","generated_ts":1784292049,"stale":0}}
수치 대조: /api/posts?date=오늘이 postsToday=10, doneCount=2(type=done), /api/todos?date=오늘이 todoDone=1/todoTotal=4 — 브리핑 문장과 일치.4) LaunchAgent load + kickstart 즉시 트리거 (로컬):
$ plutil -lint ~/Library/LaunchAgents/org.crowny.today-worker.plist
/Users/ef/Library/LaunchAgents/org.crowny.today-worker.plist: OK
$ launchctl load ~/Library/LaunchAgents/org.crowny.today-worker.plist
$ launchctl kickstart -k gui/$UID/org.crowny.today-worker
[2026-07-17 21:38:05] === 워커 시작 (VPS_URL=http://127.0.0.1:9604) ===
[2026-07-17 21:38:05] 큐 응답: {"ok":true,"queue":[]}
[2026-07-17 21:38:05] 처리할 큐 없음 — 종료
(직전 로컬 테스트에서 이미 큐를 소진했으므로 빈 큐 정상 처리 확인 — 재적재 후 재실행으로 처리 경로도 확인함, 위 2)/3) 참조)5) 브리핑생성.한선 자체시험 (16/16 PASS):
CT_SELFTEST=1 ./crownyc run 브리핑생성.toau
=== 브리핑생성 자체시험 ===
PASS J값 기본 ... (총 16건 전부 PASS)
=== 자체시험 끝 ===
브리핑 예시 텍스트 전문
오늘 팀은 게시물 10건 중 2건을 마쳤고, 진행 중인 항목이 7건 있습니다. 할일은 1/4건 완료로 정상 범위입니다.
과부하/막힘 케이스 예시(로컬 합성 데이터, 실제 계산 확인용):
오늘 팀은 게시물 6건 중 3건을 마쳤고, 진행 중인 항목이 2건 있습니다. 막힘으로 표시된 항목이 1건 있어 확인이 필요합니다. 할일은 4/10건 완료로 정상 범위입니다.
잔여 이슈
- claude -p 다듬기 경로(큐 3건 초과 시)는 스로틀 게이트 연동 코드까지 작성했으나, 이번 검증에서는 큐가 항상 1건이라 다듬기 분기가 실행되지 않았다 — 별도로 3건 이상 큐를 인위 적재해 실측 필요.
_인증확인의 워커토큰 우회는 조회 전용(GET posts/todos) 목적이나, 현재 서버 코드상 모든 인증필요 라우트(게시물 생성 등)에도 동일하게 통한다 — role=admin 합성이라 위험은 낮지만, 다음 손 볼 때 워커토큰을 GET 전용으로 더 좁히는 편이 안전.stale판정 임계값(21600초=6시간)은 스펙 그대로 반영, 실측 재확인은 6시간 경과 후에나 가능(미검증, 로직상 경과=지금-ts 계산은 맞음).- 브리핑 카드(todayBridge.js)는 CDP/실브라우저 렌더 스크린샷으로 확인하지 않음 — node --check(문법)만 통과. 실제 CrownyAI/웹 렌더 확인은 후속.
- team scope 외 개별 user 브리핑(
scope=user&user=이름)은 코드 경로상 지원하나 이번 검증에서 team만 실측.
크라우니코드 lookup/learn
crownycode-learn.sh search "AI 워커 큐 브리핑" 사전 조회(기존 매치 없음) → 완료 후 crownycode-learn.sh add "AI워커_큐브리핑_토큰0조립" ... 로 4상 판정 브리핑 조립 패턴 학습 등록함(별칭 "AI워커" 등록됨).