POSA_Copyrighter/docs/INDEX.md
changukyu 847f7a852d fix: 게시 호스트를 영속화에서 복구하고 콘솔·LLM 에 노출
P1 3번("매칭 호스트 기반 신뢰출처 구분")을 착수하며 전제를 검증했더니 틀렸다.
호스트는 "이미 저장돼 있고 점수만 안 쓰는" 게 아니라 저장 시점에 파괴되고
있었다.

  google_custom_search.py:240,296 이 실호스트(displayLink)를 data["domain"] 에
  넣는데, store_serialization.py:298 이 data["provider"] 를 우선하므로 그 값이
  API 이름으로 덮어써진다. Vision·Naver 는 호스트를 아예 안 넣고 URL 만 준다.
  결과적으로 저장된 모든 행의 domain 이 "google" 이고, LLM 프롬프트 12줄이 전부
  domain=google 이었다 — 저작권 심사에서 가장 결정적인 "어디에 떠 있나"를 모델도
  운영자도 볼 수 없었다. 같은 케이스의 payload 55행 중 41행이 url 을 갖고 있는데도.

domain 필드는 재사용할 수 없다. provider 커버리지 동기화와 CSE 쿼리 시그니처
매칭이 그 값을 키로 쓰고(store_serialization.py:195, 599) 콘솔도 provider
라벨로 렌더한다. 그래서 host 를 별도 키로 신설했다.

  _result_host(): data["domain"] 이 provider 와 다르면 그것을, 아니면 결과/페이지
  URL 에서 호스트를 파싱한다. www 제거·소문자화. 파싱 실패는 "호스트 없음"이지
  영속화 실패가 아니다.

  점수에는 영향이 없다 — RiskScorer 는 domain 도 host 도 읽지 않는다. 증거 id 도
  안 바뀐다: _evidence_id 는 도메인 객체의 data 를 해싱하고 payload 를 해싱하지
  않으므로 payload 키 추가가 id 를 움직이지 않는다.

  PROMPT_DATA_FIELDS 의 domain -> host. 화이트리스트 크기는 그대로.

  콘솔은 증거 카드 제목 줄에 monospace host-chip 을 붙인다. 기존 저장 행에는 host
  키가 없으므로 evidenceHost() 가 저장된 URL 에서 파생한다 — 292행 마이그레이션도,
  bootstrap 마다 행당 파싱도 필요 없다(렌더되는 행만 지나간다).

실렌더 검증(127.0.0.1:9512, 실 DB)에서만 드러난 레이아웃 결함 2건을 고쳤다.
코드 리딩으로는 보이지 않았다.
  1) 긴 CDN 호스트(cloudfront-ap-northeast-1.images.arcpublishing.com)가 제목 줄
     폭을 다 먹어, evidence-title 의 overflow-wrap:anywhere 와 결합해 제목이 한
     글자씩 세로로 깨졌다. 칩을 max-width 20ch 로 묶고 제목에 flex:1 1 auto 를 줘
     남는 폭을 갖게 했다.
  2) 그 수정 후에는 반대로 biz.chosun.com(14자)까지 잘렸다. 칩을 flex:0 0 auto 로
     고정했다 — 제목("구글 full image match found")은 전 행이 동일한 상용구이고
     호스트가 그 행의 실제 내용이므로, 들어갈 수 있는 호스트가 잘리는 쪽이 잘못이다.
  최종: 칩 82개 전부 유효 호스트 형태(TLD com/edu/hk/jp/kr/net/org), 가시 칩 41개 중
  잘리는 것 4개(모두 20자 초과 CDN), 콘솔 에러 0.

PROVENANCE 배점은 보류한다. 두 조건이 다 안 맞는다. (a) detect_screen_capture 의
카테고리가 geometry 와 subtitle_band 둘뿐인데 2개를 요구하므로 라벨의 실제 의미는
"geometry AND 자막"이고, 자막 없는 방송 캡처는 구조적으로 도달 불가다 — 임계를
낮추는 게 아니라 독립적인 세 번째 신호가 필요하고 그건 새 탐지기다. 한계를 실측과
함께 상수 옆에 기록했다. (b) "새 탐지 신호는 0점 라벨로 출시한 뒤 decision_stats
상관을 보고 가중치 부여"가 2026-07-25 결정이고, 판정 0건인 지금 배점하면 그 결정을
위반한다.

신뢰출처 판정 자체는 "어느 도메인이 우리 것/라이선스 계약처인가"라는 운영자
데이터가 있어야 한다. 호스트가 살아났으므로 목록만 정해지면 cleared_reference 와
같은 패턴(점수 0 + 사유 노출)으로 붙는다.

테스트 4건 추가. pytest 574 passed(착수 전 570), 린트 클린.
2026-08-09 08:51:12 +09:00

9.5 KiB

Project INDEX

  • 2026-08-09 · P1 2차 — 게시 호스트 복구. P1 3번의 전제("호스트는 저장돼 있고 점수만 안 쓴다")가 틀렸다: CSE 가 실호스트를 data["domain"] 에 넣는데 store_serialization.py:298 이 provider 를 우선해 저장 시점에 덮어썼고, Vision·Naver 는 URL 만 준다. 결과적으로 LLM 프롬프트 12줄이 전부 domain=google 이라 "어디에 떠 있나"를 모델도 운영자도 못 봤다. domain 은 provider 커버리지·CSE 시그니처 매칭의 키라 재사용 불가 → host 별도 키 신설(_result_host: CSE displayLink 또는 URL 파싱, www 제거·소문자). 점수 무영향·_evidence_id 무변화(도메인 객체 해싱이라 payload 키 추가가 id 를 안 바꿈). PROMPT_DATA_FIELDS 의 domain→host, 콘솔 증거 카드에 monospace host-chip(레거시 행은 evidenceHost() 가 저장 URL 에서 파생 — 마이그레이션 불요). 실렌더 검증(실 DB, 칩 82개 전부 유효 호스트, 콘솔 에러 0)에서만 드러난 결함 2건 수정: 긴 CDN 호스트가 제목을 한 글자씩 세로로 깨뜨림 → 칩 20ch 상한 + 제목 flex:1 1 auto, 이후 짧은 호스트까지 잘림 → 칩 flex:0 0 auto 고정. PROVENANCE 배점은 보류(카테고리가 2종뿐이라 라벨이 사실상 "geometry AND 자막"이고, "새 신호는 0점 출시 후 상관 확인" 결정과 충돌 — 한계를 provenance.py 상수 옆에 기록). pytest 574 passed(착수 전 570)

  • 2026-08-09 · P1 1차 — 판별력 복구를 위한 신호 풀 도입, SCORER_VERSION v1 → v2 — 포화 동인이 둘이었다: category 없는 raw Vision 라벨이 이름별 버킷으로 무한 누적(WEB_ENTITY_MAX_POINTS=20, medium 미만으로 캡)되고, full/partial/page/visual 이 하나의 사실을 4개 상관된 뷰로 보고하는데 합산돼 125→100 포화(WEB_PRESENCE_MAX_POINTS=60, high 미만으로 캡). category(character/celebrity/work) 엔티티는 권리 분류이므로 풀에서 제외하고 +45 유지. 원칙: 웹에 존재한다는 사실만으로는 high 가 되지 않고, 두 번째 종류의 신호가 필요하다. 224조합 재측정 — 웹 신호만으로 high 가 되는 조합 2/7 → 0/7, 100점 포화 66.5%→51.3%(전체 high 비율 82.6%→76.3%은 격자가 균등샘플이라 대리값이 아님을 명기). 웹에 없는 오리지널·cleared 자산이 100/high → 20/low 로 교정. 라운드트립 버킷 보존 테스트는 캡에 가려지지 않도록 저신뢰도로 재작성. pytest 570 passed(착수 전 565) → 평가 리포트 6절

  • 2026-08-08 · 기능 실효성 평가(6축 조사 + 축별 적대적 반증, 에이전트 12) 후 P0 4종 수정 — 결론은 "검색·기록 노동은 줄여주나 심사 건수는 못 줄인다"(신호조합 224개 전수 시 high 77.7%·low 3.6%, 지배 동인은 match 스택이 아니라 web entity 누적 — 이미지 매칭 0건이어도 entity 3개면 100/high, 웹에 없는 오리지널도 66/medium). 실수정: 폴더 정리가 판정 기록을 지우던 경로 차단(판정 완료 가드 + assetMissing 1회 마킹; 실증으로 export 1행→0행 확인됨), 비상 차단 스위치 실배선(_external_calls_blocked 신설 → 자동 Naver/CSE/Vision/얼굴크롭 5개 경로; 이전엔 눌러도 외부 호출 5회 동일), 생체 크롭의 보존기간 초과 생존 차단(스냅샷 제외 + 레거시 아카이브 복구 스킵 — 복구가 mtime을 리셋해 90일을 재시작시켰음), 오프라인 설치 문서의 계약 위반 모델 정정(0.5b→7b + ollama 바이너리·GPU 전제·거짓 disable 서술). DB 6건은 첫 커밋보다 2주 앞선 구빌드 스냅샷이라 검증 코퍼스가 아님을 확인. pytest 565 passed(착수 전 562), 린트 클린 → docs/reviews/2026-08-08-utility-assessment.md

  • 2026-08-08 · 프로젝트 분석 후 인프라·구조·보안 정비 — 원격 백업 복구(20커밋이 로컬에만 있었음: main 2 + 작업브랜치 18 → Forgejo push), 단일 verify 게이트 신설(verify.ps1 + pytest.ini + ruff.toml, ruff 0.12.0 dev 핀; 린트 18건 정리), app.js 5,428줄 → app-*.js 15개 분할(순수 라인범위 추출로 바이트 단위 라운드트립 검증, 로드 순서를 정적 테스트로 고정), 보안 2종 실수정(SSRF DNS-rebinding TOCTOU — 단일 DNS 해석 후 그 답변으로만 연결하는 _guarded_create_connection; 실패 인증 스로틀 — 자격증명 제시 실패만 10회/60초, 429+Retry-After), 7/11 체크리스트 재확인(6건은 이미 수정돼 있던 낡은 항목, Batch 8 은 Batch 6 과 중복이라 정리), CLAUDE.md·docs/PROJECT_MAP.md 신설. E2E 플레이크(업로드 대기 10초 → UPLOAD_ANALYSIS_TIMEOUT_MS 30초) 해소. pytest 562 passed(착수 전 533/1 failed), 린트 클린. 실렌더 확인: 큐·케이스 심사 정상 렌더, 스크립트 15개 전부 200, 새로고침 후 콘솔 메시지 0

  • 2026-07-24 · 멀티에이전트 프로젝트 점검(에이전트 109, 적대적 검증 포함) — pytest 377/379, 런타임 스모크 9/9 통과, 신규 CONFIRMED 43건(HIGH 7) + 7/11 체크리스트 중 3건 실수정 확인 + 기능 제안 상위 5 선정 → docs/reviews/2026-07-24-project-review.md

  • 2026-07-26 · 운영자 콘솔 실렌더 검증 + 결함 5종 수정 (d20e6d6) — 서버 기동 불가(cp949에서 em-dash UnicodeEncodeError로 프로세스 사망, 납품 환경 치명) · 요약이 모델이 못 본 78건을 "연결 근거"로 주장(B단계가 절반만 막았음) · grounded/droppedClaims 미영속화 · app.js가 서버 그룹 분류를 자체 매핑으로 덮어씀 · 기본 모델이 클라이언트/배선에서 불일치(미설치 모델 지목). 실렌더 확인: 출처 표기 그룹 자체 섹션 렌더, [E1,E2...] 인용 2단 요약, sourceIds 78→10, qwen2.5:7b 6.0초. pytest 534 passed

  • 2026-07-25 · C단계: 출처 신호 2종 (5a9fcc0) — EXIF/IPTC 권리 표기 추출(화이트리스트 6필드, GPS·시리얼·Make/Model 미판독) + 방송캡처 포렌식(레터박스·자막밴드·프레임크기, 독립 카테고리 2개 이상). 하나의 EvidenceSource.PROVENANCE·하나의 GUI 그룹으로 배관 12곳 비용 1회 지불. 둘 다 0점 라벨(캘리브레이션 전). 실측: 실제 이미지 6장에서 캡처 오탐 0·권리메타 히트 0(정밀도 확인, 가치는 이 코퍼스에서 미입증). XMP/C2PA는 defusedxml 미반입으로 제외. pytest 531 passed

  • 2026-07-25 · 실모델 프로브로 형식 예시 반향 결함 발견·차단 (6bbaaf6) — qwen2.5:0.5b가 프롬프트의 형식 예시를 그대로 출력하며 실제 라벨 12개를 전부 인용해 집합 포함 검사를 통과. 가짜 transport 테스트로는 불가능한 발견. 기본 모델을 실측으로 qwen2.5:7b 확정(0.5b 55.7s 계약위반 / 7b 8.8s 준수 / qwen3:8b 35.7s 준수)

  • 2026-07-25 · B단계: LLM을 요약기에서 증거 판독기로 (6be263a) — 입력 화이트리스트 9필드+상위 12건 상한(promotion_reason 제외), format=json 구조화 인용 + 라벨 집합 포함 검사로 미인용 claim 폐기, 기본 모델 0.5B→3B 및 num_ctx/num_predict/keep_alive/timeout 노출, 시드 LLM 호출을 _write_lock 밖으로(시드당 20건 상한). 잠금 이동이 만든 provider 커버리지 회귀(요약 존재하는데 not_run 보고)를 나레이션 후 재계산으로 수정. pytest 515 passed

  • 2026-07-25 · 실사용 투입 전 T0 4종 + 임베딩 정책 개정 구현 — (T0-1) initialRiskScore/initialRiskBand/scorerVersion 각인, (T0-2) YuNet 얼굴검출 교체 + 인물 등급(identifiable/background) 도입 및 라운드트립 보존, (T0-3) 판정 시점 피처 스냅샷(라벨 순환 차단), (T0-4) 모델·프롬프트 버전 + latency_ms 스탬프. Scene Embedding Policy 신설로 임베딩 금지 범위를 생체 식별로 축소(얼굴 임베딩 금지는 landmark·descriptor 추가로 강화). YuNet 가중치는 models/(gitignore) + offline-install.md에 sha256 반입 절차. pytest 502 passed(착수 전 463). 실측: 6장 기준 검출 378ms(YuNet) vs 654ms(Haar), 2장에서 등급 판정이 갈림

  • 2026-07-25 · AI 기능 확장 브레인스토밍(에이전트 14: 5렌즈 발산 30건 → 적대적 실현가능성 검증 6건 REFUTED → 심사 3인 채점) — 1위 「LLM 예산·계측·입력 경계」 7.90(만장일치). 검증 중 실측으로 확인: 판정 라벨 0건·operatorStatus 292행 전부 None·밴드 high 4/medium 2/low 0(4건 100점 포화)·_perceptual_hash는 pHash가 아닌 8x8 평균해시. 문서의 WAL 미설정 주장은 검증 결과 오류로 정정(이미 적용됨) → docs/brainstorms/2026-07-25-ai-capability-expansion-brainstorm.md

  • 2026-07-24 · 위 점검의 전체 remediation + 신규 기능 5종 구현 — HIGH 7·MEDIUM 15·LOW 21 및 F1~F5(키보드 심사·일괄 판정/메모 프리셋·지문 중복 인덱스·판정 내보내기·백업 복구) 완료. cv2 환경 드리프트 복구(OpenCV 5.0.0 제거). pytest 457 passed(착수 전 377/2 failed), 실서버 스모크 및 백업 복구 왕복 검증. GUI 스펙의 "일괄 승인/반려 금지(v1)" 제약은 가드레일 조건부로 개정 이력 남기고 해제 → docs/reviews/2026-07-24-implementation-checklist.md