Skip to content

refactor: ScopeResolver 제거 — 시멘틱 federation을 KV store로 일원화#237

Open
thrcle wants to merge 7 commits into
CausalInferenceLab:masterfrom
thrcle:feat/unify-semantic-storage
Open

refactor: ScopeResolver 제거 — 시멘틱 federation을 KV store로 일원화#237
thrcle wants to merge 7 commits into
CausalInferenceLab:masterfrom
thrcle:feat/unify-semantic-storage

Conversation

@thrcle
Copy link
Copy Markdown
Contributor

@thrcle thrcle commented Jun 7, 2026

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 확인

thrcle and others added 7 commits June 7, 2026 15:40
- 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)로 교체
Copy link
Copy Markdown
Collaborator

@seyoung4503 seyoung4503 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔗 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/ScopeResolverterm_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)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 결정 필요 — 전사(guild) 등록/clearis_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 ""
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

questionchannel_id = thread_id or channel_id라 스레드에선 채널이 아니라 스레드 ID로 조회돼요. 부모 채널에서 등록한 channel 용어가 스레드 질문엔 안 잡힐 수 있는데, 스레드를 부모 채널로 정규화하는 게 의도에 맞지 않을까요? 🤔


saved_terms: list[str] = []
for t in terms:
term = str(t.get("term", "")).strip()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 nitterm_custom: 포함 term을 거부하는데(semantic_federation.py:146) org_setup은 검증이 없네요. LLM이 : 든 term을 뱉으면 cterm: 키가 깨져 _load_allsplit(":",3)이 레이어를 오판 → 조회에서 조용히 누락될 수 있어요. 동일 검증 추가를 제안합니다.

@thrcle
Copy link
Copy Markdown
Contributor Author

thrcle commented Jun 8, 2026

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 키 파싱 보호)
bot.py:151 — /term_custom remove에 layer 파라미터 추가 (guild/channel 용어도 슬래시로 삭제 가능)
term_wizard.py:70 — defer() 를 try 블록 안으로 이동, webhook 만료 시에도 무한로딩 방지

@seyoung4503
Copy link
Copy Markdown
Collaborator

🔍 추가 검증 — Codex 2차 교차검토로 2건 더 확인했습니다

앞선 체인 리뷰 이후 remove 경로와 빈 channel 처리를 Codex로 한 번 더 파봤는데, 짚을 게 하나, 확인 부탁드릴 게 하나 나왔어요. 🙏

짚고 싶은 것 (🟠)/term_custom action:remove로 전사·채널 용어가 안 지워집니다
slash remove가 layer를 항상 member로 고정해서, guild/channel 용어는 등록은 되는데 삭제 경로가 없어요.

  • bot.py:152 — remove 호출 시 layer 미전달
  • commands.py:170 — 핸들러 기본값 layer="member" 그대로 dispatch
  • semantic_federation.py:157-161 — 그 layer로 삭제 키 생성 → cterm:{term}:member:{user_id}만 삭제 (guild·channel 키는 타겟 안 됨)

remove에 layer(또는 scope) 인자를 받거나, effective lookup으로 적용 중인 layer를 찾아 지우는 식이면 어떨까요?

한번 확인해주셨으면 (❓) — channel 레이어 쓰기에 빈 entity 가드가 없습니다
channel_id가 빈 상태로 channel 레이어 저장이 들어오면 cterm:{term}:channel:(빈 entity)가 생겨, 실제 채널 lookup에서 안 잡히는 항목이 될 수 있어요.

  • semantic_federation.py:157 / org_setup.py:115entity = channel_id에 빈 값 폴백 없음
  • bot.py:42 — channel_id는 interaction.channel이 있을 때만 채워지고 guild_id는 독립 세팅이라, guild_id 有 + channel_id 빈 조합이 코드상 가능

DM은 scope가 dm:{user_id}로 분리돼 길드와 안 섞이는 건 확인했고, Discord가 실제로 빈 channel_id를 주는 케이스가 있는지는 코드만으론 판단이 안 되더라고요. 혹시 그런 입력이 실제로 들어올 수 있는지 한번 봐주시면 좋겠습니다. 있으면 guild 폴백/등록 거부 가드가 필요할 것 같아요.

(참고: 삭제된 define_metric.py에 같은 취지의 빈-entity 폴백 픽스가 있었는데, 파일 제거와 함께 빠져서 살아남은 두 도구엔 미반영된 상태입니다.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants