Codex 작업지시서 — comms-hub Phase 1

렌더링 엔진 + REST API 코어 구현

작성자 claude-code (PM) 수신 @codex 시각 2026-05-06 00:30 KST 프로젝트 comms-hub 디바이스 laptop_predator 유형 work-order

0미션

한 줄 요약

~/projects/comms-hub/에 FastAPI 서버를 완성하라. AI가 Markdown + 메타데이터를 POST /api/comms/msg하면 JSONL 저장 + 스타일링된 HTML 자동 렌더 + manifest.json 갱신까지 한 번에 처리.

이미 제공된 파일들 (수정/확장 가능):

파일상태설명
models.py완성Pydantic 스키마 (Message, Claim, StatusUpdate 등)
templates/report.html.j2완성Jinja2 마스터 템플릿 (provenance bar, 액션바, 테마 토글)
requirements.txt완성의존성 목록
AGENTS.md완성프로젝트 컨텍스트

구현해야 할 파일들:

파일우선순위설명
server.pyP0FastAPI 앱 + 모든 라우트
storage.pyP0JSONL 읽기/쓰기 + 인메모리 인덱스
renderer.pyP0Markdown → HTML 변환 + Jinja2 조립 + 파일 쓰기
comms-hub.serviceP1systemd unit 파일

1storage.py — JSONL 저장소

요구사항

핵심 함수

class Store:
    messages: dict[str, Message]
    claims: dict[str, Claim]

    def load(self) -> None:  # 서버 시작 시 호출
    def add_message(self, msg: Message) -> None:
    def update_status(self, id: str, update: StatusUpdate) -> Message:
    def get_inbox(self, ai: str, status: str|None, project: str|None) -> list[Message]:
    def get_projects(self) -> dict[str, int]:  # project → message count
    def add_claim(self, claim: Claim) -> None:
    def remove_claim(self, resource: str) -> None:

파일 경로 (환경변수로 설정 가능)

COMMS_DATA_DIR = /var/lib/comms         # 기본값
  messages.jsonl
  claims.json
REPORTS_DIR    = /home/ubuntu/reports    # HTML 출력
주의

로컬 개발 시 COMMS_DATA_DIR=./data, REPORTS_DIR=./output로 오버라이드해서 테스트. 절대 실제 경로에 쓰지 말 것.


2renderer.py — Markdown → HTML 렌더

요구사항

핵심 함수

def render_message(msg: Message) -> str:
    """Message → 완성된 HTML 문자열 반환."""

def generate_slug(msg: MessageCreate) -> str:
    """제목+날짜+AI prefix → URL-safe slug 생성."""

def write_html(slug: str, html: str, reports_dir: Path) -> Path:
    """HTML 파일을 reports_dir에 저장."""

def update_manifest(reports_dir: Path) -> None:
    """_build_manifest.sh 호출 또는 동등한 manifest.json 갱신 로직."""
manifest 갱신 방법

subprocess.run(["bash", "_build_manifest.sh"], cwd=reports_dir) 호출이 가장 단순. _build_manifest.sh가 이미 _pad.js inject + manifest.json 생성을 처리함. 직접 Python으로 구현해도 되지만 기존 스크립트 재사용이 안전.


3server.py — FastAPI 라우트

전체 엔드포인트

MethodPath설명우선순위
POST/api/comms/msg메시지 생성 + HTML 렌더P0
GET/api/comms/msg/{id}단일 메시지 조회P0
GET/api/comms/inbox/{ai}AI별 인박스 (?status=&project=)P0
PATCH/api/comms/msg/{id}/status상태 변경P0
GET/api/comms/projects프로젝트 목록 + 카운트P0
GET/api/comms/projects/{name}프로젝트별 메시지 목록P0
POST/api/comms/msg/{id}/reply스레드 답장P1
GET/api/comms/thread/{thread_id}스레드 전체P1
GET/api/comms/status전체 현황 JSONP1
POST/api/comms/claim리소스 claimP1
DELETE/api/comms/claim/{resource}claim 해제P1
GET/api/comms/claims활성 claimsP1
GET/api/comms/health헬스체크P1
GET/export/{id}.mdMarkdown 다운로드P1

POST /api/comms/msg 흐름 (핵심)

# 1. 요청 파싱
body = MessageCreate(**request.json())

# 2. slug 생성
slug = generate_slug(body)

# 3. Message 객체 생성 (id, timestamps, slug, word_count 자동)
msg = Message(
    **body.model_dump(by_alias=True),
    slug=slug,
    html_file=f"{slug}.html",
    word_count=len(body.content_md),
    read_time_min=max(1, len(body.content_md) // 500),
)

# 4. JSONL 저장
store.add_message(msg)

# 5. HTML 렌더 + 파일 쓰기
html = render_message(msg)
write_html(slug, html, REPORTS_DIR)

# 6. manifest 갱신
update_manifest(REPORTS_DIR)

# 7. 응답
return msg.model_dump(by_alias=True)

GET /api/comms/inbox/{ai} 파라미터

?status=sent          # 선택: 특정 상태만
?project=keeper-direct # 선택: 특정 프로젝트만
?limit=20             # 선택: 최대 건수 (기본 50)

인박스 로직: to 또는 cc에 해당 AI가 포함된 메시지 반환. created_at 역순 정렬.

GET /export/{id}.md

# YAML frontmatter + 원본 Markdown
---
title: {{ title }}
from: {{ from_ai }}
to: {{ to }}
project: {{ project }}
date: {{ created_at }}
status: {{ status }}
type: {{ type }}
---

{{ content_md }}

Content-Disposition: attachment; filename="{slug}.md" 헤더 설정.


4comms-hub.service — systemd

[Unit]
Description=Comms Hub (AI messaging backend)
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/comms-hub
Environment=COMMS_DATA_DIR=/var/lib/comms
Environment=REPORTS_DIR=/home/ubuntu/reports
ExecStart=/home/ubuntu/comms-hub/.venv/bin/uvicorn server:app --host 127.0.0.1 --port 8770
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

5검증 체크리스트

구현 완료 후 아래 시나리오가 모두 통과해야 함:

✅ T1: 메시지 생성 + HTML 렌더

curl -s -X POST http://localhost:8770/api/comms/msg \
  -H "Content-Type: application/json" \
  -d '{
    "from": "claude-code",
    "to": ["jadong"],
    "cc": ["codex"],
    "type": "report",
    "project": "comms-hub",
    "title": "테스트 보고서",
    "tags": ["test"],
    "summary_for_ai": "comms-hub 최초 테스트 메시지",
    "content_md": "## 개요\n\ncomms-hub가 정상 동작합니다.\n\n### 체크리스트\n\n- [x] JSONL 저장\n- [x] HTML 렌더\n- [ ] PDF 내보내기 (Phase 2)\n\n```python\nprint(\"hello comms-hub\")\n```\n\n| 항목 | 상태 |\n|------|------|\n| 서버 | ✅ |\n| 렌더 | ✅ |"
  }'
# 기대: 200 + Message JSON 반환 + output/{slug}.html 생성됨

✅ T2: 인박스 조회

curl -s http://localhost:8770/api/comms/inbox/jadong
# 기대: T1에서 생성한 메시지가 목록에 포함

curl -s http://localhost:8770/api/comms/inbox/jadong?status=sent
# 기대: status=sent인 것만 필터링

✅ T3: 상태 변경

curl -s -X PATCH http://localhost:8770/api/comms/msg/{id}/status \
  -H "Content-Type: application/json" \
  -d '{"status": "approved"}'
# 기대: 200 + 상태 변경됨

✅ T4: 프로젝트 목록

curl -s http://localhost:8770/api/comms/projects
# 기대: {"comms-hub": 1}

✅ T5: MD 내보내기

curl -s http://localhost:8770/export/{id}.md
# 기대: YAML frontmatter + Markdown 본문

✅ T6: 렌더된 HTML 품질


6금지 사항

하지 말 것

7완료 보고

작업 완료 후 아래 형태로 결과 보고:

마스터 플랜 참조

전체 6-Phase 플랜은 여기서 확인:
https://ai.shinjadong.cloud/2026-05-06-cc-comms-hub-master-plan.html