ADR 0044 — 한 줄 본질 가시화 layer 정형화
Context
너홀로프로의 한 줄 본질: "법률 사무소가 자기 데이터로 자기 AI 를 학습·활용해, 소송을 더 빠르고 정확하게 관리" (CLAUDE.md SSoT). 모든 기능·결정의 우선순위는 이 한 줄에 기여하는가로 판정.
이 본질을 사용자/운영자에게 시각적으로 전달하는 layer 가 ADR 0043 v2.67 ~ v2.71 (5 PR) 동안 점진 추가됐다:
- v2.67 dashboard "학습 회귀 통과율" stale indicator (마지막 갱신 시각 + 10분 amber)
- v2.68 dashboard 14일 RAG history line chart (recall + precision + trend label)
- v2.69 per-tenant chart (tenants/[tid] 새 "학습 회귀" tab) + hook SSoT 분리
- v2.70 web 사용자 dashboard "사무소 AI 학습 효과" 카드 (chart 없는 trend 텍스트)
- v2.71 anomaly hypothesis history toggle (latest 1 + previous 9)
이 5 PR 의 공통 패턴이 재사용 가능한 layer 디자인 임을 확인. 향후 한 줄 본질 직결되는 새 데이터 source (예: 변호사 finalize 후 학습 효과 측정, exemplar selection 적중률, refinementFeedback 누적) 가 추가될 때 동일 layer 패턴을 따라야 일관성 + 사용자 학습 비용 0.
본 ADR 은 그 5 단계를 정형화한 가이드라인 + 신설 본질 source 추가 시 invariant 강제.
Decision
1. layer 5단계 표준
본질 가시화가 필요한 새 데이터 source 는 다음 5 layer 중 source-audience 매트릭스에 따라 강제 layer 를 ship 해야 본 줄 본질에 정합.
v1.1 amendment (2026-05-11) — source 의 audience 별 강제/선택 layer 분리:
| audience | 의미 | L1 | L2 | L3 | L4 | L5 | 예시 |
|---|---|---|---|---|---|---|---|
| 사용자 가치 | 사무소 owner/staff 가 본인 dashboard 에서 직접 가치 인지 | 강제 | 강제 | 강제 | 강제 | 강제 | ragMergeQuality (학습 효과) |
| 운영자 도구 | ops 콘솔 에서만 의미, 사용자 노출 안 됨 | 강제 | 강제 | 강제 | skip OK | 강제 | anomalyHypotheses (가설 진단) |
| 운영자 알림 (tenant-aware) | tenant 별 분류 가능한 production 결함 | 강제 | skip OK | 강제 | skip OK | 강제 | (예정) |
| 운영자 알림 (global) | tenant-agnostic 전역 결함 (v1.2 추가) | 강제 | skip OK | skip OK | skip OK | 강제 | active alerts (config/opsAnnouncements) |
audience 분류 규칙:
- 사용자가 메뉴에서 직접 클릭해서 보는 카드인가? → 사용자 가치
- 운영자가 정확도/품질 trend 를 비교하는가? → 운영자 도구
- production 사고 catch 가 본질이고 dashboard 보다 알림이 우선인가? → 운영자 알림
본질 source 신설 시 audience 분류 → 강제 layer 만 ship → invariant 추가. L4 강제가 적용되는 source 만 apps/web 변경 필요 (의존 규칙 비용 절감).
| layer | 위치 | 의도 | 예시 (v2.67~v2.71) |
|---|---|---|---|
| L1 stale indicator | latest 1 건 표시 옆 inline | 운영자가 데이터 신선도 즉시 인지 (10분 amber + pulse) | dashboard 학습 회귀 axis (v2.67) |
| L2 시계열 chart | 14일 line/area chart | "측정 시작 후 어떻게 변하고 있나?" 시각 답 (recharts ChartContainer + var(--chart-N) 색역) | dashboard RagHistoryChart (v2.68) |
| L3 per-tenant view | tenants/[tid] 의 새 tab 또는 카드 | ops 가 사무소별 trend 비교 가능 (hook SSoT 분리, drift 0) | tenants/[tid] "학습 회귀" tab (v2.69) |
| L4 사용자 가시 시그널 | apps/web dashboard "학습·리서치" 그룹 | 사무소 owner/staff 본인이 자기 사무소 학습 trend 직접 확인 | DashboardLearningEffectCard (v2.70) |
| L5 latest + history | 토글로 expand | 운영자 cross-time 비교 (이번 vs 이전 N개) | anomaly hypothesis history (v2.71) |
2. 강제 invariant
각 layer 마다 정적 grep 회귀 가드 (apps/ops/__tests__/*-invariants.test.ts 또는 apps/web/__tests__/*-invariants.test.ts):
- L1 stale:
STALE_THRESHOLD_MS = 10 * 60 * 1000+setInterval(..., 60 * 1000)+bg-amber-500 animate-pulse - L2 chart:
ChartContainer+ rechartsLineChart또는AreaChart+ Y 축 percent 또는 적절 unit + trend label (🟢/🔴/⚪ 또는 동등) - L3 per-tenant: hook 이
tenantId: stringarg 받음 +tenants/${tenantId}/{collection}path 사용 (cross-tenant 격리) - L4 web 카드:
useTenant().tenantId로 자기 사무소 격리 +handleListenerErrorSSoT (ADR 0029 fail-loud 정합) - L5 history: latest 1 + previous N toggle +
aria-expanded+ 부트스트랩 모드 (data < 2) 비표시
3. 부트스트랩 모드 (필수)
production 데이터 0 건 시 모든 layer 가 noise 0 으로 silent (사용자에게 빈 카드/혼란 X). 데이터 N 건 누적 후 자동 노출:
- L1 —
lastFetchedAt === null시 indicator 비표시 - L2 —
series.length < 2시 chart 비표시 - L3 — per-tenant 데이터 없으면 "측정 대기" 안내 표시
- L4 —
series.length < 2시 카드 비표시 - L5 —
history.length < 2시 toggle 비표시
4. SSoT 분리 강제
같은 hook/컴포넌트가 ops + web 또는 dashboard + per-tenant 양쪽에 쓰이면 _lib/{name}.ts 또는 _lib/use-{name}.ts SSoT 분리 (drift 0). 의존 규칙 위반 시만 복제.
예 (v2.69 패턴):
apps/ops/app/(ops)/dashboard/_lib/rag-merge-history.ts(pure data: 상수 + type)apps/ops/app/(ops)/dashboard/_lib/use-rag-merge-history.ts(hook: useCollectionSnapshot SSoT)apps/ops/app/(ops)/dashboard/_components/RagHistoryChart.tsx(UI 컴포넌트)- 호출자 (dashboard/page.tsx + tenants/[tid]/page.tsx) 모두 위 SSoT import
5. ADR 0029 fail-loud 정합
L1~L5 모든 hook 은 useCollectionSnapshot / useDocumentSnapshot SSoT 사용 (apps/ops/lib/realtime/use-firestore-snapshot.ts 또는 apps/web/lib/firebase/listener-error.ts). 자체 onSnapshot 직접 호출 + silent error handler 금지 (PR #1885 + Block 1 sweep + Block A sweep 정합).
Consequences
긍정
- 사용자 학습 비용 0 — 새 본질 source 가 추가돼도 layer 패턴 동일이라 사용자가 새로 배울 게 없음
- drift 0 — SSoT 분리 + invariant 정적 가드로 dashboard/tenants/web 간 표시 불일치 차단
- 부트스트랩 noise 0 — production 데이터 0 건 시 빈 카드 보이지 않음 (메모리
feedback_no_pilot_mock_only정합) - fail-loud 자동 정합 — useCollectionSnapshot SSoT 사용 강제로 silent swallow 회귀 방지
부정
- 5 layer ship 의 PR 부담 — 새 본질 source 는 5 PR (≈10시간) 필요. trade-off: 일관성 vs 빠른 ship
- chart 비용 — recharts 가 web/ops 양쪽에 있어 번들 크기 영향. 단 sparkline 같은 경량 형태로 mitigation 가능
- ops + web 양쪽 변경 — 의존 규칙 (
apps/* 간 import 금지) 으로 컴포넌트 복제 발생. SSoT 분리는 _lib 단위까지만 가능
회귀 가드 invariant 신설
apps/ops/__tests__/essence-layer-checklist-invariants.test.ts— 신규 본질 source 가 5 layer 모두 ship 했는지 정적 체크리스트 (L1~L5 패턴 grep)apps/web/__tests__/essence-layer-web-invariants.test.ts— apps/web 측 L4 카드 패턴 (useTenant + handleListenerError + 부트스트랩 분기) 정적 검증
적용 사례 — 현 ship 본질 source
v1.1 amendment 정합 후 표 (audience 컬럼 추가):
| source | audience | L1 | L2 | L3 | L4 | L5 | 강제 layer 정합 |
|---|---|---|---|---|---|---|---|
| ragMergeQuality | 사용자 가치 | v2.67 ✓ | v2.68 ✓ + v2.72 sparkline ✓ | v2.69 ✓ | v2.70 ✓ + v2.72 sparkline ✓ | v2.73 ✓ | 100% (5/5) |
| anomalyHypotheses | 운영자 도구 | v2.74 ✓ | v2.75 ✓ | v2.74 ✓ | n/a (skip) | v2.71 ✓ | 100% (4/4) |
| active alerts | 운영자 알림 (global) | v2.83 ✓ | n/a | n/a | n/a | v2.83 ✓ | 100% (2/2) |
| draftDiffs (편집률) | 사용자 가치 | v2.76 ✓ | v2.76 ✓ | v2.77 ✓ | 이미 ship (DashboardFirmLearningCard) | v2.78 ✓ | 100% (5/5) |
| docgenEvents (성공률) | 사용자 가치 | v2.79 ✓ | v2.80 ✓ | v2.80 ✓ | v2.81 ✓ | v2.80 ✓ | 100% (5/5) |
| refinementFeedback (exemplar) | 사용자 가치 | v2.82 ✓ | v2.82 ✓ | v2.82 ✓ | v2.82 ✓ | v2.82 ✓ | 100% (5/5) |
| draftDiffs.citation (AI 인용 보존율) | 사용자 가치 | cycle 7 ✓ | cycle 7 v2 ✓ | cycle 7 v3 ✓ | cycle 7 v3 ✓ | cycle 7 v2 ✓ | 100% (5/5) — finalize-document-action 자동 캡처 + ops dashboard 5번째 axis + sparkline + L5 history list + 사용자 가시 DashboardCitationPreservationCard + per-tenant TenantHealthCard 통합. |
v1.3 추가 (자율 세션 #9 cycle 7, 2026-05-12): 5번째 source draftDiffs.citation (AI 인용 보존율) — computeDraftCitationDiff pure function 으로 변호사의 AI 인용 채택 비율 측정. finalize-document-action 이 자동 캡처. 한 줄 본질 학습 루프 quality 의 새 차원 — RAG hallucination 자동 catch.
cycle 7 v2 (2026-05-12 후속): L2 sparkline + L5 history list ship — color: ratio < 0.3 rose / ratio < 0.7 amber / ratio ≥ 0.7 emerald (anomaly 후보 자동 시각화).
cycle 7 v3 (2026-05-12 후속): L3 + L4 ship — DashboardCitationPreservationCard (web 사용자 가시) + TenantHealthCard 통합 (ops per-tenant 분포). draftDiffs.citation 100% (5/5) 정합 달성 — 5번째 source 가 4 source 와 동등 layer 정형 보유.
적용 사례 — ragMergeQuality 100% 정합 (2026-05-11 v2.73): 5 layer 모두 ship 완료된 첫 source. dashboard latest + stale + 14일 chart + per-tenant + 사용자 가시 카드 + sparkline + history list 토글까지 누락 없음. 본 source 가 후속 source 의 layer 정형 모범.
v1.4 status (자율 세션 #9 cycle 14, 2026-05-12): 7 source 표 전체 정합 종합 — 7/7 source 100% layer 정형 달성. 한 줄 본질의 4 axis (편집률·docgen 성공률·exemplar selection·학습 회귀) + 5번째 axis (AI 인용 보존율) + 운영자 도구 (anomalyHypotheses) + 운영자 알림 (active alerts) 모두 ship.
다음 단계 후보:
- 6번째 axis 후보 — citation accuracy (인용 정확도, citation-existence verify 결합), draftDiffs heavy-edit 비율, 또는 portal 사용자 만족도 (의뢰인 측 axis)
- CI 통합 — adr-0044-source-hook-sync invariant 가 7 source 모두 정합 검증, 신규 source 추가 시 invariant 와 hook + dashboard 4곳 (L1·L2·L3·L4·L5) + CLAUDE.md ADR 동기화 강제
- 사용자 onboarding — 5 axis section header collapse panel (cycle 7 v3-2) 의 다국어화 또는 영구 표시 옵션
관련 ADR
- ADR 0029 fail-loud 정책 (precondition)
- ADR 0040 ops 재정의 (4 axis 이론)
- ADR 0042 anomaly closed-loop (L5 패턴 source)
- ADR 0043 v2.67~v2.71 (본 ADR 의 ship 사례)