OKR 콘텐츠 자동 집계 구현 계획
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: KR3-1(콘텐츠 편수)/KR2-2(활성채널)/KR3-3(브랜드채널) 수동 입력 완전 제거, 시스템 자동 집계 구현
Architecture: content-os DB에 content_sites 중앙 집계 테이블 추가. 각 사이트 배포 스크립트에서 발행 수 UPDATE. okr-collector가 SUM/COUNT 쿼리로 자동 집계.
Tech Stack: Turso(LibSQL), Node.js, TypeScript, okr-collector.ts
현황 분석
5개 사이트 블로그 소스 현황
| 사이트 | 블로그 DB | 발행 수 |
|---|---|---|
| apppro.kr | apppro-kr Turso DB | ~40편 |
| KoreaAI Hub | content/blog/ MDX 파일 | ~10편 |
| richbukae.com | content/blog/ MDX 파일 | ~7편 |
| ai-architect-global | content/blog/ MDX 파일 | ~9편 |
| prompt-shop | content/guides/ MDX 파일 | ~3편 |
- content-os blog_posts: 발행 0개 (apppro.kr 블로그 = WordPress blog.apppro.kr, 별도)
- koreaai-hub DB: subscribers/newsletter_issues만 있음 (블로그 테이블 없음)
- 핵심 문제: 분산된 소스 → 단일 자동 집계 불가
선택한 설계: content_sites 중앙 집계 테이블
각 사이트 배포/발행 시 → content_sites 테이블 UPDATE → okr-collector SUM
Task 1: content-os DB에 content_sites 테이블 생성
Files:
- Create:
projects/content-orchestration/migrations/add-content-sites.sql
Step 1: SQL 마이그레이션 파일 작성
CREATE TABLE IF NOT EXISTS content_sites (
site TEXT PRIMARY KEY,
display_name TEXT NOT NULL,
published_count INTEGER NOT NULL DEFAULT 0,
is_active INTEGER NOT NULL DEFAULT 1,
last_published_at INTEGER,
last_synced_at INTEGER,
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);
-- 초기 데이터 시딩 (현재 수동 카운트 반영)
INSERT OR REPLACE INTO content_sites (site, display_name, published_count, is_active, last_synced_at, updated_at) VALUES
('apppro-kr', 'apppro.kr 블로그', 40, 1, unixepoch(), unixepoch()),
('koreaai-hub', 'KoreaAI Hub 블로그', 10, 1, unixepoch(), unixepoch()),
('richbukae', 'richbukae.com 블로그', 7, 1, unixepoch(), unixepoch()),
('ai-architect', 'AI Architect Global 블로그', 9, 1, unixepoch(), unixepoch()),
('prompt-shop', 'prompt-shop /guides', 3, 1, unixepoch(), unixepoch());
Step 2: content-os DB에 실행
turso db shell content-os < projects/content-orchestration/migrations/add-content-sites.sql
Expected: 테이블 생성 + 5개 row 삽입
Step 3: 확인
turso db shell content-os "SELECT site, published_count, is_active FROM content_sites;"
Expected: 5개 사이트 row, 총 published_count = 69
Step 4: Commit
git add projects/content-orchestration/migrations/add-content-sites.sql
git commit -m "feat: add content_sites table for OKR auto-aggregation"
Task 2: okr-collector.ts 수정 — KR3-1/KR2-2/KR3-3 자동 집계
Files:
- Modify:
projects/jarvis-system/apps/worker/src/okr-collector.ts
Step 1: CONTENT_OS_DB_URL/TOKEN 환경변수 추가
Worker .env 파일에 추가:
CONTENT_OS_DB_URL=libsql://content-os-migkjy.aws-ap-northeast-1.turso.io
CONTENT_OS_DB_TOKEN=<token>
→ turso db tokens create content-os 로 토큰 생성
Step 2: okr-collector.ts — content-os DB 연결 추가
// content-os DB 연결 (OKR 콘텐츠 집계용)
import { createClient } from '@libsql/client';
let _contentOsClient: ReturnType<typeof createClient> | null = null;
function getContentOsClient() {
if (!_contentOsClient) {
const url = process.env.CONTENT_OS_DB_URL;
const authToken = process.env.CONTENT_OS_DB_TOKEN;
if (!url || !authToken) throw new Error('CONTENT_OS_DB_URL/TOKEN not set');
_contentOsClient = createClient({ url, authToken });
}
return _contentOsClient;
}
async function queryContentOs(query: string): Promise<number> {
try {
const client = getContentOsClient();
const result = await client.execute(query);
const row = result.rows[0];
const val = (row as any)?.value ?? (row as any)?.cnt ?? 0;
return Number(val) || 0;
} catch (err) {
console.error('[OKR] content-os query failed:', err);
return 0;
}
}
Step 3: KR3-1, KR2-2, KR3-3 collector를 manualCollector에서 자동 집계로 교체
{
krId: 'KR3-1',
configKey: 'content_published',
collect: async () => {
const value = await queryContentOs(
"SELECT SUM(published_count) as value FROM content_sites WHERE is_active = 1"
);
return { value, raw: { source: 'content_sites', query: 'SUM(published_count)' } };
},
},
{
krId: 'KR2-2',
configKey: 'content_channels',
collect: async () => {
const value = await queryContentOs(
"SELECT COUNT(*) as value FROM content_sites WHERE is_active = 1"
);
return { value, raw: { source: 'content_sites', query: 'COUNT active sites' } };
},
},
{
krId: 'KR3-3',
configKey: 'brand_channel_reach',
collect: async () => {
const value = await queryContentOs(
"SELECT COUNT(*) as value FROM content_sites WHERE is_active = 1"
);
return { value, raw: { source: 'content_sites', query: 'COUNT active sites' } };
},
},
Step 4: Worker 빌드 + 재시작
cd projects/jarvis-system/apps/worker
npm run build
# Worker 프로세스 재시작 (기존 프로세스 종료 후)
Step 5: 즉시 수동 collectAll 트리거 + 결과 확인
turso db shell kanban "SELECT id, current_value FROM okr_key_results WHERE id IN ('KR3-1','KR2-2','KR3-3');"
Expected: KR3-1=69, KR2-2=5, KR3-3=5
Step 6: Commit + Push
git add projects/jarvis-system/apps/worker/src/okr-collector.ts projects/jarvis-system/apps/worker/.env
git commit -m "feat: auto-aggregate KR3-1/KR2-2/KR3-3 from content_sites table"
git pull --rebase origin main && git push
Task 3: 각 사이트 배포 시 content_sites 자동 업데이트 스크립트
Files:
- Create:
scripts/sync-content-count.sh
#!/bin/bash
# 사용법: ./scripts/sync-content-count.sh <site> <count>
# 예: ./scripts/sync-content-count.sh apppro-kr 41
SITE=$1
COUNT=$2
if [ -z "$SITE" ] || [ -z "$COUNT" ]; then
echo "Usage: $0 <site> <count>"
exit 1
fi
turso db shell content-os "UPDATE content_sites SET published_count = $COUNT, last_synced_at = unixepoch(), updated_at = unixepoch() WHERE site = '$SITE';"
echo "✅ content_sites[$SITE] = $COUNT"
향후 각 사이트 블로그 발행 시:
./scripts/sync-content-count.sh koreaai-hub 11 # 새 글 발행 후 수동 호출
→ okr-collector 6시간 이내 자동 반영 (또는 즉시 collectAll 트리거)
Step 1: 스크립트 생성 + 실행 권한
chmod +x scripts/sync-content-count.sh
Step 2: Commit
git add scripts/sync-content-count.sh
git commit -m "feat: add sync-content-count.sh for content_sites update"
git pull --rebase origin main && git push
Task 4: Worker .env에 CONTENT_OS_DB_TOKEN 추가
Step 1: token 생성
turso db tokens create content-os
Step 2: Worker .env 업데이트
CONTENT_OS_DB_URL=libsql://content-os-migkjy.aws-ap-northeast-1.turso.io
CONTENT_OS_DB_TOKEN=<생성된 토큰>
Step 3: Worker 재시작 확인
curl -s http://127.0.0.1:5555/status | python3 -m json.tool
완료 기준
- content_sites 테이블 생성 + 초기 데이터 5개 row
- KR3-1 자동 집계: SUM(published_count) = 69
- KR2-2 자동 집계: COUNT(active sites) = 5
- KR3-3 자동 집계: COUNT(active sites) = 5
- okr_key_results에 자동 반영 확인
- 수동
UPDATE okr_key_results명령 더 이상 불필요
주의사항
- KR4-3 (평균 소요일): 타임스탬프 혼용 버그 → 자동화 불가, manualCollector 유지
- KR1-x, KR2-1, KR2-3, KR3-2: 외부 플랫폼 API 연동 필요 → 별도 Phase 2 과제
- content-os DB token은 .env에만 저장, git commit 금지