디자인 검증 → 보고서 파이프라인 통합 → 운영 가시성 → 사용자 제어. 한 흐름의 네 단계가 하루에 메인에 들어갔다.
각 버전은 바로 앞 버전의 결과를 받아 다음 단계를 푼다. 디자인 → 코드 → 운영 → 사용자 — 같은 기능이 네 면을 가진다는 점을 시간이 보여준다.
samples/theme_mono_map_chart.html 1 file, 747 lines.
d3.geoTransform 으로 maplibre map.project() 를
d3-geo path projection 에 위임 — move/resize 이벤트마다 SVG 오버레이가 따라옴.
d3.geoInterpolate 64 분할로 great-circle arc.
#C9A84C 단일 하이라이트 원칙이 양 테마에서 작동함을 확인.
버건디 베이스 타일은 dark_nolabels 가 너무 어두워 처음에 바다·육지 구분이 안 됐고,
voyager_nolabels + brightness 0.78 로 교체해서 해결 — 첫 사용자 피드백 1회로.
map BlockType — 보고서 파이프라인 통합geopolitical_strategic archetype 의 "전장·행위자" 섹션에 자동 포함, 데이터 없으면 자동 스킵.
six_act_theater) 는
그대로 보존하면서, 신규 archetype 은 maplibre 길로 일원화. 두 길을 동시에 가져가는 것이
과도기에 가장 안전한 선택.
visual_analyst 의 leaflet_config 재사용 —
분석 흐름 코드 변경 0. build_map_payload() 가 leaflet 의 [lat,lng] 를
maplibre 의 [lng,lat] 로 변환하면서 highlight 룰·legend 자동 생성.
src/models.py:BlockType Literal 확장src/templates/blocks/map.html 신설src/templates/static/maps.{js,css} (정적 자산 동기화 대상)src/visual_builder.py:build_map_payload() 변환기src/agents/report_synthesizer.py:_payload_map() + _BLOCK_BUILDERS 등록src/tests/test_map_block.py — 13 케이스map BlockType 이 실제로 렌더하는 모습. 페이지 안에 직접 임베드.
/status 에 git branch · commit · dirtygit rev-parse --abbrev-ref HEAD /
--short=7 HEAD / git log -1 --format=%cd /
git status --porcelain 4 호출로 git 상태 한 번 캡처해
BUILD_INFO 에 보관. 시작 로그 + 텔레그램 /status 응답 양쪽에 노출.
BUILD_INFO 는 *실행 중인* 커밋을 가리키므로 미스매치를 즉시 인지.
# /status 응답 ✅ 봇 실행 중 — v3.4.1 브랜치: main 커밋: 181a620 (2026-05-01 15:33) ⚠️ uncommitted 가동시간: 0시간 5분 ...
/status 가 verifiable. 운영자가 더 이상 어느 버전이 도는지 헷갈리지 않음.
비-git 환경 / repo 외부에서는 모두 "?" 로 graceful degrade.
/stop · /stopall — 진행 중 분석 중단/stop — 진행 중 1건만 cancel. 큐는 보존 (다음 건 자동 진행)/stopall — 진행 중 cancel + 큐 전체 비움_run_analysis 시작 시점에 self._current_task = asyncio.current_task() 캡처.
핸들러는 그 task 의 cancel() 호출 → CancelledError 가 위로 전파.
Python 3.8+ 부터 CancelledError 는 BaseException 상속 →
orchestrator/agent 의 except Exception: 블록을 통과해서
_run_analysis 의 전용 except CancelledError 까지 도달, 사용자 알림 후 re-raise.
/stop handler └─ self._current_task.cancel() └─ raises CancelledError into _run_analysis() └─ propagates up through await orchestrator.run_analysis() └─ propagates up through await agent.run() (LLM call) └─ subprocess.terminate() # Claude CLI gets SIGTERM └─ finally: cleanup + reply "🛑 분석 중단됨"
v3.4.0 의 map 블록이 어떻게 분석 흐름에 끼워 들어갔는지.
회색 부분은 기존 코드, 골드 부분은 v3.4.0 신규.
적당히 측정 가능한 것들. 의도된 것보다는 작게, 의도하지 않은 것보다는 크게.