본문으로 건너뛰기

ADR-0035: Intel 데이터의 워크스페이스 소유 모델 — 셀프서비스 생성 + 마켓플레이스 배포

  • 상태: 승인
  • 날짜: 2026-08-03
  • 관련: #2747 (본 결정), #1018 (워크스페이스 격리 — 본 ADR 로 intel 도 NOT NULL 방향에 정합), #2469 (Bronze dedup), ADR-0032 (마켓플레이스 publish 스코프)

배경

POST /api/v1/intel/sources/{id}/run 한 번 안에서 Bronze 는 실행자(caller) 워크스페이스, Silver 는 소스 소유 워크스페이스로 태깅된다(#2747). 5렌즈 분석 + prod 실측:

  • Bronze 는 이미 3파티션으로 분열 — NULL 158,523행(6/20 이전 레거시) / finance-invest 124,961행 / energy-kogas 40행(admin 이 X-Workspace-Slug 헤더를 바꾼 채 실행한 흔적).
  • dedup(#2469)이 파티션 안에서만 돌아 macro 4,000행 중 3,900행이 파티션 간 동일 content_hash 중복. dedup 의 재적재 억제(articles 일별 4,800→200행, 24배)는 파티션이 갈리는 순간 무효화된다.
  • "전사 공유"의 표현이 평면마다 다르다 — PG Silver Fact 는 NULL, Iceberg silver 자산은 BIGINT 센티넬 0, Bronze 는 표현 없이 finance-invest 강제 폴백(auth/workspace_fence.py:36,113-120).
  • 구 설계(docs/DESIGN_INTEL_PLATFORM.md:16 "Fact 는 NULL=전사")와 #1018 M3("NULL 은 백필 후 제거") 가 충돌한 채 공존한다.

기각한 대안

  • (전사-NULL 안) Fact 데이터를 workspace_id IS NULL 전사 공유 1급으로 확정 — #1018 의 NOT NULL 전환과 영구 충돌하고, "모두에게 자동 공개"라 배포 통제권이 없다. 플랫폼의 공유 모델(마켓플레이스 publish/구독, ADR-0032)과도 이질적이다.
  • (B) Silver 를 caller 기준으로 통일 — Fact UNIQUE 가 workspace 독립 + ON CONFLICT DO NOTHING 이라 "먼저 돌린 사람이 테넌트를 결정"하는 비결정 상태가 되고, 라이선스 READ 가드(silver.py:456)가 소스 소유를 정본으로 쓰므로 유료 열람권이 실행자에 종속된다.
  • (C) 현행 유지 — 중복이 계속 쌓이고, 읽기 펜스를 켜는 순간 흩어진 데이터가 그대로 드러난다. 미룰수록 재태깅 비용만 커진다.

결정

Intel 은 "플랫폼 전사 데이터"가 아니라 워크스페이스가 소유·운영하는 데이터 제품이다. 금융 인텔리전스 워크스페이스(finance-invest)의 사용자가 소스를 만들고, 그 워크스페이스 관리자가 외부 배포를 결정한다.

D-1. Intel 데이터 평면은 워크스페이스 소유(NOT NULL) — NULL 전사 표현을 폐기한다

iceberg.bronze.intel_*_raw 와 PG Silver 전체(Fact 포함)에서 workspace_id 는 소유 워크스페이스를 가리킨다. "NULL = 전사 공유" 표현은 사용하지 않으며, intel 평면도 #1018 의 NULL 백필 → NOT NULL 전환 스코프에 포함한다 (충돌 해소 — group_filter.py:64,66 의 "M2 백필 후 NULL 분기 제거" 방향 그대로). 구 설계 문서 §4.6 의 "Fact=NULL 전사" 규정은 본 ADR 로 대체한다.

D-2. Bronze 스탬프는 caller 가 아니라 자원 소유(IntelSource.workspace_id) 기준

sdk.intel.write_bronze 에 자원 소유 스탬프 시맨틱을 신설하고 관리형 run 경로가 src.workspace_id 를 전달한다. PS 셀프서비스 sink 가 이미 쓰는 패턴(run_id → PipelineStudioRun.workspace_id, body 불신 D10)과의 정합이다.

  • Silver 는 이미 소스 소유 기준 → 두 레이어가 같은 값을 본다(#2747 해소).
  • X-Workspace-Slug 헤더/admin impersonation 이 스탬프에 영향을 주지 못한다 (energy-kogas 40행 재발 차단).
  • 실행 인가는 D-3 의 워크스페이스 권한 모델이 담당하고, caller-소스 불일치 스탬프 검증은 불필요해진다(스탬프가 소스에서 파생되므로).

D-3. IntelSource 는 워크스페이스 사용자가 생성하고 워크스페이스 안에서 운영한다

  • 생성·수정·실행: 소속 워크스페이스 멤버(admin 전용 페이지에서 ws 권한 모델로 전환). 소스는 생성자의 워크스페이스로 귀속(workspace_id NOT NULL).
  • 기존 관리형 소스 535건은 이미 finance-invest 소유 — 이 모델의 첫 사례가 되며, NULL 소유 6건은 finance-invest 로 백필한다.
  • 데이터 조회(인텔리전스 메뉴·query/search 도구)는 소유 워크스페이스 멤버 기준으로 펜스된다.

D-4. 외부 공유는 워크스페이스 관리자의 마켓플레이스 배포로 한다

intel MCP 도구(search_<domain>/query_<domain>)의 타 워크스페이스 노출은 자동이 아니라 소유 ws 관리자의 publish 결정이다 — 기존 마켓플레이스 구독 메커니즘(ADR-0032, P3.2 #2047: 구독 실행은 SOURCE 워크스페이스 컨텍스트, 결과만 공유) 을 그대로 사용한다. Bronze ws-펜스는 완전일치를 유지한다 (구독 경유 실행이 소유 ws 컨텍스트에서 돌므로 OR IS NULL 확장 불필요).

D-5. 마이그레이션 — 소유 워크스페이스로 수렴 (Iceberg UPDATE 가능 확인됨)

  1. Bronze: NULL 158,523행 + energy-kogas 40행 → finance-invest(소스 소유) 재태깅 — dedup 이 단일 소유 파티션으로 수렴, 24배 억제 유지.
  2. Silver Fact 잔여 NULL(ohlcv 20행) → finance-invest.
  3. IntelSource NULL 6건 → finance-invest 백필 후 컬럼 NOT NULL 전환(#1018 정합).
  4. 레거시 무펜스 MCP 도구 펜스 도입이 필수가 된다query_intel_filingsWHERE 1=1 도구는 전사 데이터 가정에서만 무해했다. 데이터가 워크스페이스 소유가 되는 순간 무펜스 = 격리 위반이므로, 재태깅과 같은 단계에서 소유 ws 펜스를 건다(구독자는 마켓플레이스 경유).
  5. intel/market UI 의 공유 파티션 술어(workspace_id = 0 OR IS NULL)도 소유 ws 기준으로 교체. ★ 이 단계는 아래 "스코프 밖" 의 센티넬 통일 (#3194) 이 선행해야 한다 — 술어만 먼저 바꾸면 읽는 쪽이 기대하는 표현을 쓰는 쪽이 아직 쓰지 않아 market UI 가 빈 화면이 된다(쓰기 pipelines=수동 배포, 읽기 API=자동 배포라 동시 전환이 불가능하다 — docker-build.yml 트리거 경로에 apps/api/** 는 있고 pipelines/** 는 없다).

스코프 밖 (후속 이슈)

  • Iceberg silver 자산의 BIGINT 센티넬 0 — UUID 소유 ws 로 통일: #3194. BIGINT 는 UUID 를 담을 수 없어 소유 행이 공유 파티션에 섞이는 일이 구조적으로 불가능하므로 활성 유출이 아니라 표현 불일치다(대상도 공개 시세 데이터). Iceberg 는 BIGINT→VARCHAR 타입 승격을 허용하지 않아 컬럼 교체가 필요하고, "공유" 를 어떤 값으로 표현할지(전용 ws UUID vs 별도 규약) 가 미결이다.
  • 다중 워크스페이스가 같은 도메인을 수집할 때의 교차-ws Bronze dedup 정책 (현재는 소유 파티션 내 dedup 으로 충분).

결과

  • 한 워크스페이스 안에서 생성→수집→검증→배포가 완결되는 데이터 제품 모델이 된다. 공유는 기본값이 아니라 관리자의 명시적 배포 행위다.
  • #2747 의 비결정성(실행자에 따라 데이터 위치가 바뀜)이 소멸하고, #1018 NOT NULL 전환과 정합된다 — NULL 특례 분기가 늘지 않고 줄어든다.
  • 트레이드오프: 타 워크스페이스 사용자의 "자동" 조회가 사라진다 — 구독 경유로 전환되며, 이는 의도된 통제다. 기존에 무펜스 도구로 intel 을 읽던 사용자는 마이그레이션 단계 4 이후 구독이 필요하다.
  • 마이그레이션 규모: Bronze 재태깅 158,563행(UPDATE, MergeWriter 확인), Silver 20행, 소스 6건 + 읽기 경로 펜스 도입(레거시 MCP 도구·market UI).