From 546bd54700a319bf15592c9ac271601be65c61fa Mon Sep 17 00:00:00 2001
From: th-kim0823
Date: Mon, 27 Apr 2026 19:51:35 +0900
Subject: [PATCH] feat: DEFAULT_TOPICS_SEED + ensure_topics_seeded
Co-Authored-By: Claude Sonnet 4.6
---
assign_teams.py | 85 +++++++++++++++++++++++++++++++++++++++++++++++++
tests/e2e.py | 22 +++++++++++++
2 files changed, 107 insertions(+)
diff --git a/assign_teams.py b/assign_teams.py
index cba18c9..6b2359c 100644
--- a/assign_teams.py
+++ b/assign_teams.py
@@ -12,6 +12,90 @@ import random
from collections import Counter, defaultdict
from pathlib import Path
+DEFAULT_TOPICS_SEED = [
+ {
+ "id": "T1",
+ "title": "내 인생 시간 도둑 처단기",
+ "tagline": "매일 짜증나는 반복 작업 하나를 2시간 안에 박살내기.",
+ "tone": "실용 + 살짝 치트키",
+ "items": [
+ "Slack 멘션 자동 분류/요약기 — '진짜 날 부른 거' vs 'FYI' 분리",
+ "Jira 티켓 1줄 자동 요약 + 다음 액션 제안기",
+ "회의 캘린더 → 하루 시작 브리핑 (\"오늘 3개 있고 2개는 안 가도 됨\")",
+ "PR 리뷰 우선순위 큐 (크기/긴급도/차단여부 기반)",
+ "반복 쿼리/kubectl 명령 매크로 CLI — 자주 치는 10개를 1글자로",
+ "온콜 노이즈 필터 — 진짜 볼 알람 vs 무시해도 되는 알람",
+ "\"이번 주 내 활동 자동 요약\" — PR/티켓/리뷰 통합 리포트",
+ "Grafana 자주 보는 패널 즐겨찾기 통합 뷰",
+ "Slack 스레드 장문 요약기 — 놓친 채널 따라잡기용",
+ "\"이 회의 들어가야 함?\" 분류기 — 캘린더 제목·참석자 기반 추천",
+ ],
+ },
+ {
+ "id": "T2",
+ "title": "벼르던 사이드 프로젝트",
+ "tagline": "평소 \"저거 하나 만들고 싶은데\" 하던 개인 토이를 2시간 안에 작은 완성품으로.",
+ "tone": "몰입 + 작은 완결성",
+ "items": [
+ "내 PR/커밋 패턴 분석 개인 대시보드 (시간대/요일/사이즈 분포)",
+ "팀 Wiki/Notion을 터미널에서 fzf 스타일로 검색하는 CLI",
+ "사내 모델/데이터셋 메타데이터 검색 프로토타입",
+ "git 히스토리 인터랙티브 시각화 뷰어",
+ "\"오늘 내가 한 일\" 자동 일기 생성기 (커밋/PR/티켓 통합)",
+ "PR 코멘트 감정/톤 분석으로 팀 리뷰 문화 리포트",
+ "로컬 Kubernetes 리소스 관계 그래프 실시간 시각화",
+ "사내 논문/테크 문서 RAG 검색 도구",
+ "터미널에서 차트 포함된 마크다운 뷰어",
+ "북마크/링크 자동 분류·태깅 개인 도구",
+ ],
+ },
+ {
+ "id": "T3",
+ "title": "오버엔지니어링 선수권",
+ "tagline": "평소 안 써본 무거운 기술 패턴을 일부러 작은 문제에 적용해 배우기.",
+ "tone": "학습형 오버엔지니어링",
+ "items": [
+ "Todo 앱에 이벤트 소싱 + CQRS 제대로 적용",
+ "간단한 계산기 서비스에 OpenTelemetry 풀 트레이싱 구축",
+ "문서 검색 기능에 벡터 DB + 하이브리드 검색 (BM25 + 임베딩)",
+ "파일 업로드에 S3 presigned URL + 체크섬 검증 + 재시도 로직 정식 설계",
+ "팀 투표 기능을 Raft 합의 알고리즘으로 구현",
+ "회의실 예약을 Kafka 이벤트 스트리밍 기반으로",
+ "로컬 개발 환경을 완전한 K8s 매니페스트 (Deployment/Service/Ingress/HPA)로 재현",
+ "LLM + RAG 기반 PR 자동 리뷰 봇 아키텍처 설계·구현",
+ "멀티 에이전트 협업(프롬프트 2~3단계)으로 간단한 의사결정 시스템",
+ "사이드카 패턴으로 로깅/메트릭/인증 분리 데모",
+ ],
+ },
+ {
+ "id": "T4",
+ "title": "팀에게 주는 작은 선물",
+ "tagline": "동료를 돕는 도구/봇/사이트. 특정인을 놀리는 게 아니라 팀 전체를 위한 것.",
+ "tone": "실질적 도움 + 가벼운 온기",
+ "items": [
+ "배포 상태 집계·알림 봇 (성공/실패/롤백 요약)",
+ "신입 한 주 서바이벌 가이드 자동 생성기 (온보딩 링크/문서 수집)",
+ "팀 내부 용어/약어 사전 봇 — 신입/리서처 친화",
+ "아침 브리핑 봇 — 오늘 회의/배포/만료 알람 한방",
+ "점심 투표 1분 컷 봇 — 선택지 자동 생성 후 이모지 투표",
+ "팀 반복 질문 FAQ 봇 — 같은 질문 반복되는 채널용",
+ "온콜 교대 시 인수인계 자동 요약 생성기",
+ "회의실 스마트 추천 — 인원/시간대/위치 기반",
+ "사내 서비스 변경사항 요약 구독 봇",
+ "\"이번 주 팀 지표 한 장\" 리포트 — 머지 PR, 해결 티켓, 배포 수",
+ ],
+ },
+]
+
+
+def ensure_topics_seeded(data):
+ """topics 비어있으면 default 시드. 기존 있으면 보존."""
+ cats = data.get("topics", {}).get("categories", [])
+ if not cats:
+ data.setdefault("topics", {})
+ data["topics"]["categories"] = DEFAULT_TOPICS_SEED
+
+
# 시니어 명단 (Platform/Data/HPC/System만 알고 있음)
SENIORS = {
"한지승", "손현준", "강승형", "변수민", # Platform/Data
@@ -271,6 +355,7 @@ def main():
"votes": [],
}
data["people"] = people_records
+ ensure_topics_seeded(data) # 신규
# 누락 키 보강
for k, v in [
("settings", {"voting_open": True}),
diff --git a/tests/e2e.py b/tests/e2e.py
index d77b59b..da7af96 100644
--- a/tests/e2e.py
+++ b/tests/e2e.py
@@ -278,6 +278,27 @@ def t_topics_helpers():
assert get_topics() == sample2
+def t_topics_seeded_after_assign():
+ from app import get_topics, _empty_state, save_data, load_data
+ # 빈 상태로 reset
+ save_data(_empty_state())
+ assert get_topics() == []
+
+ from assign_teams import ensure_topics_seeded
+ data = load_data()
+ ensure_topics_seeded(data)
+ save_data(data)
+
+ cats = get_topics()
+ assert len(cats) == 4
+ for c in cats:
+ assert len(c["items"]) == 10
+ assert c["id"] in ("T1", "T2", "T3", "T4")
+ assert c["title"]
+ assert c["tagline"]
+ assert c["tone"]
+
+
if __name__ == "__main__":
print(f"# E2E (data={TEST_DATA})\n")
test("hackathon.json 로드 (34명, 7팀)", t_load)
@@ -296,6 +317,7 @@ if __name__ == "__main__":
test("load_data nested 키 backfill", t_load_data_backfills_nested_settings)
test("stage 헬퍼", t_stage_helpers)
test("topics 헬퍼", t_topics_helpers)
+ test("topics 시드", t_topics_seeded_after_assign)
fails = sum(1 for r, _ in results if r == FAIL)
print(f"\n# {len(results)} 중 통과 {len(results) - fails}, 실패 {fails}")