POSA_Copyrighter/docs/INDEX.md
changukyu be5d42a418 test: 코드↔그림 리터럴 드리프트 게이트, 다이어그램을 내부 문서로 확정
용도를 내부 문서로 못박았다. 고객 인수물이 아니므로 납품 품질 기준을 적용하지
않는다 — 감사에서 나온 한글 폰트 미번들과 영어 뷰어 UI 는 archify 한계라 spec
으로는 못 고치는데, 사내 머신에는 한글 폰트가 있으므로 low 로 내렸다. 승격
조건(그 둘이 먼저 blocker)을 PROJECT_MAP 다이어그램 절에 적어 두었다.

그 위에 두 번째 게이트를 올린다. 기존 test_diagram_sync 가 "산출물이 spec 보다
낡았나"를 본다면, test_diagram_facts 는 "spec 이 코드보다 낡았나"를 본다.
그림에는 코드에서 뽑은 상수가 라벨과 카드 문장으로 하드카피돼 있어서,
SCORER_VERSION 을 올리거나 믹스인을 하나 추가하면 코드는 통과하고 그림만
조용히 틀려졌다. CLAUDE.md 가 그 값들을 규칙으로 못박아 두었는데 정작 같은
값을 그린 다이어그램에는 보호가 없었다.

  SCORER_VERSION · SCHEMA_VERSION · 믹스인 개수 · app-*.js 개수 · 바디 상한
  · 인증 실패 10회/60초 · 프롬프트 12행 상한 · 풀 상한 60/20 · 기본 모델
  · 런타임 의존성 3종

"지문 유사도 0.9+ -> 80점" 은 이름 있는 상수가 아니라 risk_scoring 안의 인라인
리터럴이라, import 대신 실제로 채점해서 확인한다. 라벨이 주장하는 값과 스코어러가
내는 값이 같은지를 직접 본다.

차단을 실증했다 — SCORER_VERSION v3->v4 로 3건, MAX_PROMPT_EVIDENCE 12->16 으로
2건이 실패한다. 영향받는 다이어그램 전부에서 잡힌다.

발견 가능성도 정리했다. 감사에서 "3.3MB 산출물이 어떤 문서 색인에도 없다"가
나왔으므로 README 색인에 등재하고, PROJECT_MAP 까지 가지 않아도 되도록
docs/diagrams/README.md 에 직접 수정 금지·재생성 명령·CSP 주의를 두었다.

검증: ruff clean, pytest 619 passed (사실 게이트 +19).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BStHFrjQj2VypecSG9ex1V
2026-09-21 11:32:12 +09:00

28 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Project INDEX
- 2026-09-21 · 다이어그램 용도를 **내부 문서로 확정**하고 그 기준으로 부채를 재판정. 한글 폰트 미번들·영어 뷰어 UI 는 archify 한계라 spec 으로 못 고치는데, 사내 머신에는 한글 폰트가 있으므로 low 로 내렸다(인수물로 승격하면 둘이 먼저 blocker — PROJECT_MAP 다이어그램 절에 승격 조건으로 명시). 이어서 **코드↔그림 리터럴 드리프트 게이트** `tests/test_diagram_facts.py` 19건 신설 — 그림에 하드카피된 코드 유래 값(SCORER_VERSION · SCHEMA_VERSION · 믹스인 8개 · `app-*.js` 15개 · 바디 10MB · 인증 10회/60초 · 프롬프트 12행 · 풀 상한 60/20 · 기본 모델 · 런타임 의존성 3종)을 실제 코드 상수와 대조하고, 상수가 아닌 "지문 유사도 0.9+ → 80점" 은 실제로 채점해 확인한다. SCORER_VERSION v3→v4 와 MAX_PROMPT_EVIDENCE 12→16 을 실제로 넣어 **영향받는 세 다이어그램 전부에서 차단됨**을 실증. 발견 가능성도 정리 — README 문서 색인에 등재, `docs/diagrams/README.md` 신설(폴더 안에 '직접 수정 금지'·재생성 명령·CSP 주의). **ruff clean · pytest 619 passed**(사실 게이트 +19)
- 2026-09-21 · 다이어그램 감사 후 **드리프트 게이트 신설**. 에이전트 6축 감사가 주간 한도로 절반(2축)만 완주했고 검증 단계도 죽어, 완주분 15건을 저널에서 복구한 뒤 결정적 주장은 직접 재검증했다. 확정된 구멍 — 그림 4종의 재생성 절차가 **문서대로는 실행 불가**(`archify` 실행파일이 PATH 에 없음), 소스↔산출물 동기화를 보는 장치 0개, 사이드카 24개가 개발 머신 절대경로를 담은 채 미추적·비ignore, 임베드 폰트에 **한글 구간 0건**(`U+AC00` 매치 0 · 폴백은 중국어 폰트뿐). 실수정: `scripts/rebuild-diagrams.ps1`(validate→deliver→visual-check→manifest, 실행 확인)과 `scripts/update_diagram_manifest.py`, 수신증 `docs/diagrams/manifest.json`, 게이트 `tests/test_diagram_sync.py` 14건(HTML 직접 수정·spec 만 수정 두 시나리오 모두 실제로 차단됨을 실증), `.gitignore` 에 사이드카 제외(미추적 24→0), `.gitattributes` 에 `linguist-generated -diff merge=binary`. 재생성해도 4종 sha256 불변 — 바이트 결정성 재확인. 한글 폰트·영어 뷰어 UI·architecture 좌표 하드코딩·lifecycle 손튜닝 8개소는 PROJECT_MAP 부채 표에 등재. **ruff clean · pytest 600 passed**(게이트 +14)
- 2026-09-19 · 다이어그램 4종으로 확장 — `system-architecture`(폐쇄망 경계·SSRF 가드 앞 호출 게이트), `operator-review-workflow`(4레인, 비상 차단 시 심사 계속 경로), `submission-lifecycle`(분석 실패·보류가 분석으로 복귀하는 두 고리)를 신설하고 기존 `evidence-dataflow` 포함 4종의 **범례 항목**을 한글화했다(렌더러 기본값이 "User UI / Agent logic / policy·PII" 같은 타 도메인 문구였다). **뷰어 크롬은 영어로 남는다** — `locale` enum 이 en/zh-CN 뿐이고 템플릿이 `<html lang="en">` 을 하드코딩하므로 Light/Dark·Present·Export·범례 제목("Legend")은 저자가 바꿀 수 없다. 9/21 감사에서 확인. 작성 중 내린 고도 판단: 아키텍처에서는 analyzer/scorer 를 한 컴포넌트로 합쳤고(세부는 dataflow 담당), 라이프사이클에서 "재분석 요청"은 decisionStatus 가 아닌 운영자 액션이라 상태에서 제외해 복귀 엣지를 하나로 줄였다. 4종 모두 archify showcase 통과(아티팩트 검사 9/9 · 합성 오류 0 · 경고 0) + visual-check 통과(1440×900 · 2048×1320 각 Light/Dark, 오버플로 0). 수신증 — dataflow html a62f0f5c / architecture f5a4b6e1 / workflow 402914a3 / lifecycle 62e934a3
- 2026-09-19 · `docs/diagrams/evidence-dataflow.html` 신설 — 증거 데이터 흐름(수집 → 경계 검사 → 정규화·채점 → 영속화 → 소비) 자체 완결 다이어그램. 9/18 리뷰 수정의 불변식을 그림으로 고정한다: **제공자별 풀·버킷**(RiskScorer v3), **라운드트립이 원 점수를 재현**(`_evidence_payload` ↔ `_domain_evidence_from_ui`), **host 는 저장 시 해석·복원 시 우선 반환**, 지식DB 지문 매칭이 풀링 밖 유일한 high 승격 경로. 프로덕션 LLM 경로가 영속화 payload 를 복원해 쓴다는 사실(`store_operations.py:487`, `EvidenceEnricher` 는 테스트 전용)도 반영. archify showcase 검증 통과(아티팩트 검사 9/9 · 합성 오류 0 · 경고 0), 브라우저 증거 통과(1440×900 · 2048×1320 각 Light/Dark 4컷, 오버플로 0, 최소 투영 텍스트 6.71px ≥ 6px). spec sha256 42c334cd · html sha256 268f3352 (822,655 B). CDN·원격 fetch 없음 — 폐쇄망 규칙 준수
- 2026-09-18 · `/code-review` 15건 전수 처리 (**헤드라인 1건은 반증**). 반증: "CSE 웹 증거가 라운드트립 후 0→45점" — 리뷰가 `match` 키 없는 합성 Evidence 로 재현한 것이고, 실제 CSE 웹 결과는 `google_custom_search.py:301` 에서 `match="page"` 를 달고 오며 `matchType` 이 충실히 왕복한다(신선/복구 모두 35점). 직렬화 불변식 위반 없음. 대신 그 조사에서 **리뷰가 놓친 라이브 버그**가 나왔다: `counted_web_buckets` 가 제공자를 안 가려 Vision 이 `match="page"` 를 먼저 보고하면 **CSE 웹 히트가 점수도 사유도 없이 통째로 버려졌다**. 실수정 — **점수 v2 → v3**(풀·버킷을 제공자별로: Vision 자기 상관 뷰 4개는 60/medium 유지, Vision full + CSE page 는 45+35=80/high 복원 · 도달 불가였던 `category` 탈출구 제거 + 미등록 유명인 미탐은 의도된 트레이드임을 명시, 승격 경로는 지식DB/FINGERPRINT), **저장·복구 5종**(`_result_host` 가 자기 host 를 안 읽어 재직렬화 시 API 이름으로 퇴행하던 잠재 버그 + 점 없는 토큰 호스트 거부 · 복구가 남긴 고아 `faceCrops` 참조를 재생성/정리 · `assetMissing` 해제 경로 신설로 두 번째 소실도 감사에 남김 · 플래그+감사를 한 트랜잭션으로 · 스코어러 마이그레이션 밴드 변경에 `scoredVersion` 스탬프 + 감사 1회), **콘솔 4종**(긴 CDN 호스트가 제목을 세로 컬럼으로 만든 flex 버그 — 실측 360px 51×95px→246×19px, **1280px 9×380px→205×19px 로 폰 전용이 아니었음** · 20ch 로 잘린 호스트를 툴팁에 · `bloggerLink` 폴백 누락 · 보존 케이스 설명 표시), **PROMPT_VERSION ko-v4-structured-host**(실모델 프로브 qwen2.5:7b — grounded 3/dropped 0/verified True, 반향 없음). 테스트 +14건. **ruff clean · pytest 586 passed**. 발견된 기존 결함 1건은 미수정 기록: 로컬 ollama 가 떠 있으면 `test_http_app.py` 3건이 실호출 타임아웃으로 실패한다(기본 생성 store 가 `127.0.0.1:11434` 기본값에 붙음 — clean HEAD 에서도 동일). 커밋 b0aa4c5 / 4ba83eb / e38dad8
- 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절](reviews/2026-08-08-utility-assessment.md)
- 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](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](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](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](reviews/2026-07-24-implementation-checklist.md)