refactor: ScopeResolver 제거 — 시멘틱 federation을 KV store로 일원화#237
Conversation
- SemanticFederationTool(/term_custom): 채널(팀)/전사/개인 3계층 용어 등록·조회·삭제 - OrgSetupTool(/org_setup): DB 스캔 + LLM으로 팀별 용어 자동 추출 → 전사(guild) 레이어 저장 - Discord UI: /term_custom 호출 시 범위 selectbox → 용어 입력 modal (2단계 UX) - 채널이 팀 경계 역할 → 팀 멤버십 관리 불필요, 충돌 설계상 없음 - lookup: 개인 > 채널(팀) > 전사(guild) (narrow→wide, git branch 방식) - kv_list_prefix() 추가 + LIKE 와일드카드 이스케이프 보안 수정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /org_setup에 team 파라미터 추가 — team 지정 시 LLM 추출 용어를 channel 레이어에 저장 - org만 지정 시 기존과 동일하게 guild 레이어에 저장 - clear도 레이어 단위로 정확히 삭제되도록 수정 - 데이터셋/로그 파일 .gitignore에 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… store - Deleted ScopeResolver, SqliteSemanticStore, semantic layer/store/scoped_layer/sql_composer - HarnessContext.scope_resolver removed; store is now sole source of truth for federation - define_metric, semantic_show rewritten to write/read FedEntry via KV _kv_key - bench demo and all tests updated; 110 tests pass Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gile assert - semantic_scope.py: remove TYPE_CHECKING import of deleted semantic.layer (caused mypy failure and runtime ImportError on get_type_hints()) - define_metric: remove [kind] prefix from stored definition; kind prefix was leaking into system prompt and Discord display verbatim - define_metric: fallback to guild layer when channel_id is empty (DM / no channel context), preventing silent invisible entries under channel:"" - define_metric: remove dead _SEMFED_PREFIX constant (duplicated _KV_PREFIX) - ecommerce_demo: replace rendered-text parsing with direct KV lookup; assert now checks mkt_def and fin_def truthy before comparing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
KV 통합 이후 define_metric이 term_custom의 부분집합이 됨. 두 툴 모두 동일한 FedEntry/KV 방식을 사용하며 기능이 중복되므로 제거. - tools/define_metric.py 삭제 - tools/__init__.py에서 DefineMetric 제거 - bot.py /define_metric 슬래시 커맨드 제거 - commands.py CommandHandlers.define_metric() 제거 - 테스트: define_metric → term_custom 사용으로 전환 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ARCHITECTURE.md: ScopeResolverPort/scope_resolver/삭제된 semantic 파일 제거, HarnessContext 설명 수정, tools 8종으로 갱신 - PROJECT.md: 도구 6종→8종, term_custom/org_setup 반영 - README.md: define_metric→term_custom, 6→8 tools 반영 - CLAUDE.md: 테스트 카운트 106→110 갱신 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /semantic_show 슬래시 커맨드 및 CommandHandlers.semantic_show() 제거
- /term_custom 파라미터 리팩토링: list_all:bool / remove:str → action:str ("show"/"remove") + term:str
- 테스트 semantic_show 호출을 term_custom(list_all=True)로 교체
seyoung4503
left a comment
There was a problem hiding this comment.
🔗 Semantic Federation 체인 통합 리뷰 (#235 → #236 → #237)
세 PR을 하나의 "채널 기반 비즈니스 용어 사전" 기능으로 통합 검토했습니다. Claude(Opus 4.8) 정독 + Codex(gpt-5.5) 교차검토.
✅ 전반 평가 — 머지 권장
방향성·구현 품질 모두 좋습니다. 테스트 110개 통과, dangling import 0, 삭제 심볼 코드 참조 0. 리팩터링으로 순 −488줄.
📈 체인이 스스로 해결한 것들
- #235→#236:
org_setup이 전사로만 저장해 팀 격리가 안 되던 blocking →team(channel 레이어) 추가로 해결. 채널 격리 실제 작동 확인 ✅ - #235→#237:
define_metric/ScopeResolver와term_custom이 같은 개념을 이중 구현하던 불일치 → KV 단일 백엔드로 통합 ✅ - 코드리뷰 버그 4건(죽은 import, kind prefix 노출, DM 빈 channel_id, federation assert false-positive) 수정 ✅
🚧 남은 항목
| 항목 | 심각도 | 상태 |
|---|---|---|
/semantic_show 죽은 안내문 (commands.py:147) |
🚨 머지 전 | 본문 참조 (라인 미변경이라 인라인 불가) |
전사 용어 권한 가드 (is_admin) |
🟠 결정 필요 | 인라인 |
| thread/channel 키 혼용 | ❓ 논의 | 인라인 |
org_setup term : 검증 |
🟡 nit | 인라인 |
ScopeResolverPort 고아 export, define_metric docstring 잔존 |
🟡 nit | 후속 |
🚨 머지 전 (commands.py:147): /connect 성공 메시지가 삭제된 /semantic_show를 안내 중 → /term_custom action:show로 바꿔주시면 어떨까요? (라인이 master부터 있어 diff에 안 잡혀 여기 적습니다)
머지 순서
#235 → #236 → (1줄 수정 후) #237. granular 히스토리가 불필요하면 #237만 머지 + 235/236 close도 가능. 🙏
| channel_id = ctx.identity.thread_id or ctx.identity.channel_id or "" | ||
|
|
||
| # team이 있으면 channel 레이어, org만 있으면 guild 레이어 | ||
| use_team = bool(team_name) |
There was a problem hiding this comment.
🟠 결정 필요 — 전사(guild) 등록/clear에 is_admin 체크가 없어요. Identity.is_admin은 이미 있는데 안 쓰입니다. 전사 용어는 모든 멤버 SQL 생성에 영향을 줘서 아무나 등록/clear 가능하면 위험해요. team/scope 분기가 생긴 지금 "전사 선택 시 관리자만" 가드를 같이 두면 좋겠습니다. (LLM이 term_custom(layer=guild)로도 부를 수 있어 가드는 툴 run() 안에 둬야 실효가 있어요.) 이번/후속 중 어디서 갈지 어떻게 생각하세요? 🔐
|
|
||
| scope = ctx.identity.guild_id or f"dm:{ctx.identity.user_id}" | ||
| user_id = ctx.identity.user_id or "unknown" | ||
| channel_id = ctx.identity.thread_id or ctx.identity.channel_id or "" |
There was a problem hiding this comment.
❓ question — channel_id = thread_id or channel_id라 스레드에선 채널이 아니라 스레드 ID로 조회돼요. 부모 채널에서 등록한 channel 용어가 스레드 질문엔 안 잡힐 수 있는데, 스레드를 부모 채널로 정규화하는 게 의도에 맞지 않을까요? 🤔
|
|
||
| saved_terms: list[str] = [] | ||
| for t in terms: | ||
| term = str(t.get("term", "")).strip() |
There was a problem hiding this comment.
🟡 nit — term_custom은 : 포함 term을 거부하는데(semantic_federation.py:146) org_setup은 검증이 없네요. LLM이 : 든 term을 뱉으면 cterm: 키가 깨져 _load_all의 split(":",3)이 레이어를 오판 → 조회에서 조용히 누락될 수 있어요. 동일 검증 추가를 제안합니다.
Blocking (완료)commands.py:147 — /semantic_show → /term_custom action:show 로 교체 결정 필요 (완료, 이번 PR에서 처리)org_setup.py:113 — guild 레이어 (org 전용) 진입 시 is_admin 체크 추가. run() 내부에 위치해 LLM 직접 호출(term_custom(layer=guild))도 막힘 논의 (완료, 부모 채널로 정규화 채택)semantic_federation.py:135 — thread_id or channel_id → channel_id 로 변경. 스레드에서도 부모 채널에 등록된 용어가 조회됨 Nit (완료)org_setup.py:199 — LLM이 : 포함 term을 뱉으면 건너뜀 (KV 키 파싱 보호) |
|
🔍 추가 검증 — Codex 2차 교차검토로 2건 더 확인했습니다 앞선 체인 리뷰 이후 remove 경로와 빈 channel 처리를 Codex로 한 번 더 파봤는데, 짚을 게 하나, 확인 부탁드릴 게 하나 나왔어요. 🙏 짚고 싶은 것 (🟠) —
remove에 layer(또는 scope) 인자를 받거나, effective lookup으로 적용 중인 layer를 찾아 지우는 식이면 어떨까요? 한번 확인해주셨으면 (❓) — channel 레이어 쓰기에 빈 entity 가드가 없습니다
DM은 scope가 (참고: 삭제된 |
- Identity.kv_scope / effective_channel_id 프로퍼티 추가 → guild_id or dm: 패턴 9곳 단일화 - semantic_federation.run()에 thread_id fallback 추가 (effective_channel_id) — 스레드에서 등록된 용어 list/remove 불가 버그 수정 - org_setup._kv_key 직접 조합 제거 → _semfed_kv_key() 위임, _SEMFED_PREFIX 상수 중복 제거 - concierge._default_llm(): /v1/ trailing slash URL이 /v1/v1/chat/completions로 doubled되는 버그 수정
- bot.py on_message: file=None을 channel.send에 전달하지 않도록 kwargs 패턴 적용 - term_wizard.py: defer()를 try 블록 밖으로 이동 — defer 실패 시 followup.send가 미승인 interaction에 호출되는 버그 수정 - org_setup.py: LLM이 synonyms를 string으로 반환할 경우 list로 정규화 - FedEntry.__post_init__: synonyms 타입 방어 추가 (string → list 자동 변환) - semantic_federation.py: _ENRICH_PREFIX/_ENRICH_RELATIONSHIPS 상수 중복 제거 → enrich_schema 임포트로 단일화 - SemanticFederationTool: 용어 등록 시 AuditEvent 발행 추가 - tests: test_edge_cases.py 신규, test_integration.py audit/scope 테스트 추가 (110 → 115개)
Summary
ScopeResolver / SqliteSemanticStore / semantic/layer|store|scoped_layer|sql_composer 전면 삭제
define_metric, semantic_show 제거 → term_custom (KV/FedEntry 기반) 단일 진입점으로 통합
HarnessContext.scope_resolver 필드 제거, store 필드만 사용 (단일 source of truth)
/term_custom 슬래시 커맨드 UX 개선: list_all: bool → action: str ("show" / "remove") 파라미터로 교체 — 유저가 의도를 명시적으로 표현 가능
코드리뷰 버그 수정
삭제된 파일(semantic/layer.py) import 잔존 → TYPE_CHECKING 블록에서도 런타임 에러 유발
define_metric이 [kind] prefix를 definition에 embed해 LLM/Discord에 노출
DM(빈 channel_id)에서 cterm:{name}:channel:로 저장 → 실제 채널 lookup에서 invisible
federation assert가 두 값이 모두 빈 문자열일 때 통과하는 false positive
docs/ARCHITECTURE.md, docs/PROJECT.md, README.md, CLAUDE.md 동기화
Motivation
ScopeResolver(별도 SQLite 테이블)와 KV store가 동일한 "git-like federation" 개념을 이중으로 구현. semantic_show는 ScopeResolver를, term_custom list는 KV를 각각 바라봐서 같은 용어를 등록해도 두 커맨드가 다른 결과를 반환하는 불일치가 존재했음. KV를 단일 백엔드로 확정하고 나머지를 제거.
Depends on
#235 (feat/semantic-federation) → #236 (feat/org-setup-team-layer) → 이 PR 순서로 머지 요청
Test plan
110개 테스트 통과 (pytest -q)
bench/ecommerce_demo.py Section 2 federation 확인 (마케팅/파이낸스 채널 정의 분리)
/term_custom (no args) → 추가 위저드 진입 확인
/term_custom action:show → 현재 scope 용어 목록 조회 확인
/term_custom action:remove term: → 삭제 확인
DM에서 /term_custom 등록 시 guild-level로 fallback 확인