2026-05-06 UI/UX 라이브 walkthrough 회고
Tier: B (production 사고 아님 — 출시 전 정합·카피·a11y·SSoT 회수) · 세션 길이: KST 23:24 (5/5) ~ 04:50 (5/6) 약 5시간 30분 · 방식: Playwright MCP + emulator + dev server 라이브 walkthrough
요지
사용자 요청 "e2e 테스트를 진행하며 모든 화면의 ui/ux의 문제점, 기능이 정확히 동작하는지, 각 텍스트의 의미를 하나하나 세세하게 확인" 에 대한 1차 walkthrough. 사이드바 10/10 메뉴 + Pack 1~5 도메인 분기 + 의뢰인 포털 양방향 + 사무실 기억 + 판례·법령 RAG + ops 외 전수 점검.
발견 사항을 두 채널로 분리:
- PR 직접 머지 (코드/규칙/회귀 테스트 7건): 즉시 해결할 수 있는 사고 root cause 와 안전망
- chip spawn (정책 결정·UI 카피·정합성 35건): 사용자 결정 큐로 push, 머지 시점은 사용자 판단
5시간 walkthrough 동안 사용자가 백그라운드에서 chip 16건 이상을 직접 머지 — 발견-fix-시각 검증 cycle 이 같은 세션 안에서 닫힘.
점검 범위 (10 메뉴 + 외부 가시면)
| 영역 | 점검 결과 |
|---|---|
| 랜딩 / 로그인 / 회원가입 / 온보딩 / 대시보드 | 인증·동의 인프라 + 빈 상태 카피 |
| 사건 등록 wizard 3단계 | 14종 recoveryType + 도메인별 라벨·필드 분기 |
| 사건 상세 9탭 (Pack 1) | 개요·채권계산·회수·서류·증거·기일·변제·대화·메모 |
| Pack 2~5 사건 등록 + 상세 | 도메인 분기 (이혼·명도·유류분·부당이득) |
| 의뢰인 포털 (외부 가시면) | 4자리 코드·메시지 양방향·시효 D-day |
사무실 기억 (/legacy) | 5컬럼 kanban + 박스 단위 업로드 모달 |
| 사이드바 5 메뉴 | 캘린더·서류 센터·정산·활동 로그·설정 |
판례·법령 (/library) | Platform RAG ADR 0021 |
의뢰인 관리 (/clients) | 사건 등록 시 자동 정리 |
직접 머지 PR 7건
| PR | 제목 | 영향 |
|---|---|---|
| #1701 | fix(rules): settlementSnapshots read rule 추가 + write Admin SDK 전용 | 채권계산 탭 listener reject + Firestore SDK INTERNAL ASSERTION FAILED (ID: ca9) 차단. 페이지 hung 해결 |
| #1702 | test(business-logic): loan-amounts KST 정합 회귀 테스트 | UTC getUTCDate() 기반 today 가 KST 자정~오전 9시 사이 daysOverdue 1일 적게 계산되던 버그 회귀 보호 |
| #1703 | fix(case-detail): 메모 탭 헤더 '내부 노트' → '내부 메모' | 탭 라벨과 패널 헤더 어휘 불일치 정합 |
| #1704 | a11y(portal): PortalLinkDialog DialogDescription 추가 | Radix DialogContent warning 2건 해소 (screen reader 진입 안내) |
| #1705 | fix(portal): '담당 법무사' → '담당 사무소' 카피 (ICP 정합) | 외부 가시면 8 파일 일괄 sweep — ADR 0034 ICP 정합 |
| #1706 | fix(portal): 의뢰인 대시보드 사건번호·청구금액·수임일 카피 정합 | docId prefix → caseNumber fallback / 0원 → "도메인 정보 입력 후 표시" / 수임일 → "사건 등록일" |
| #1707 | fix(portal): caseNumber fallback chain trim() 강화 + 헬퍼 추출 | 빈 문자열 caseNumber 케이스 안전망 (pickCaseDisplayId 헬퍼) |
진단 가치가 컸던 사고 사례 3건
사고 A — 청구액 SSoT 깨짐 (chip #1 + KST UTC 버그 합산)
증상: 같은 사건 (Pack 1 대여금) 의 청구액이 화면마다 다름.
| 화면 | 청구액 | 산식 |
|---|---|---|
| 사건 정보 영역 | 34,684,930원 | UTC 기준 약정이자 350일 + 지연손해금 125일 |
| 채권계산 탭 (회수 탭) | 34,694,794원 | KST 기준 단일 산식 476일 |
| 변제 탭 | 34,684,930원 | UTC 기준 (사건 정보 영역과 동일 경로) |
Root cause: packages/business-logic/src/cases/loan-amounts.ts:42-46 의 now.getUTCDate() — CLAUDE.md 명시 금지 패턴 (KST 유틸 사용 강제). KST 23:24 (UTC 14:24) 시점에 daysOverdue 1일 적게 계산.
Fix 경로:
- 사용자가 백그라운드에서
todayKstStr(now)helper +asOfDate?옵션 인자 시그니처로 코드 fix 머지 - 본 walkthrough 에서 PR #1702 회귀 테스트 추가 — KST 자정·정오·자정 직전 3 시점이 동일 daysOverdue 산출 검증
시각 검증: 새 사건 C-2605-001 등록 후 사건 정보 영역 = 34,694,793원 ↔ 이전 UTC 34,684,930원 차이 9,863원 = 정확히 1일치 12% 이자.
일반화된 학습: packages/business-logic 같은 패키지가 apps/web/lib/utils/kst.ts 를 직접 import 못하므로 (의존 규칙) 동등 helper 를 패키지 내부에 두거나 asOfDate?: string 인자로 받기. CLAUDE.md TZ 정합 invariant 가 packages 까지 확장 필요.
사고 B — settlementSnapshots firestore rules 누락
증상: 채권계산 탭 진입 시
[debt-settlement/snapshots] listener error: FirebaseError: No matching allow statements
@firebase/firestore (12.12.0): INTERNAL ASSERTION FAILED: Unexpected state (ID: ca9) CONTEXT: {"ve":-1}
이후 페이지 navigation 자체가 timeout (Firestore SDK 내부 state corrupted).
Root cause: firestore.rules 의 tenants/{tid}/cases/{caseId} 매치 안에 hearings·documents·recoveries·logs·evidence·comments·messages 7 종 subcollection rule 만 있고 settlementSnapshots 누락. DebtSettlementTab 의 onSnapshot 구독이 reject → SDK 12.x 알려진 internal assertion 패턴.
Fix: PR #1701 — match /settlementSnapshots/{snapshotId} { allow read: if isStaffOrOwner; allow write: if false; } (write 는 createSettlementSnapshotAction Admin SDK 전용, ADR 0003 패턴). firestore-rules-invariants.test.ts:94 이 정확히 본 패턴 검증 (write false 강제) — 테스트가 fix 의 정확성을 잡아냄.
일반화된 학습: rules invariant 테스트가 정합 차원에서 강력. SDK assertion 폭발은 rules reject 후 race 라 "rules 만 추가" 가 아니라 subcollection 별 invariant test 매트릭스 자체 를 보강하는 게 회귀 차단 방식.
사고 C — localhost:5002 hardcoded portal URL
증상: 의뢰인 포털 발급 후 받은 링크 (http://localhost:5002/portal/view?token=...) 가 dev/local 환경에서 ERR_CONNECTION_REFUSED. dev server 는 3000, ops 는 3001 — 5002 (App Hosting emulator) 는 pnpm emulator:e2e 만 켜짐.
Root cause: .env.local 의 NEXT_PUBLIC_APP_URL=http://localhost:5002 가 일반 dev 와 e2e 모두에서 사용됨. getAppBaseUrl() 코드 자체는 정상.
해결 옵션:
- A. 사용자가
.env.local을 dev 용localhost:3000으로 변경 (단순) - B.
.env.development(3000) +.env.e2e(5002) + npm script 분기 - C. 코드는
window.location.originfallback 우선 (PortalLinkDialog.tsx:55는 이미 적용,portal-token-actions.ts:84는getAppBaseUrl()사용)
.env.local 은 git ignored 라 코드 PR 으로 fix 불가 — chip 으로 남기고 사용자 결정.
작업 방식 — chip + PR 이중 채널
chip spawn (35건)
- spawn 즉시 사용자 화면에 칩으로 노출 — 한 클릭으로 별도 worktree 에서 처리 가능
- 사용자가 walkthrough 진행 중 백그라운드로 16건+ 머지 — 같은 세션 안에서 chip → 머지 → 시각 재검증 cycle 이 닫힘
- 정책 결정 (ICP 카피·도메인 균형) · UI 카피 · 안전망 · UX 정합성 같이 즉시 해결 안 되는 항목들이 chip 큐로 누적
직접 머지 PR (7건)
- root cause 가 명확한 코드/규칙 사고 + 회귀 보호 테스트
- 평균 commit ~ merge ~ 시각 검증 = 5~10분
- 모든 PR squash merge + main fast-forward sync (사용자 메모리 "PR 쌓기 금지 · 즉시 squash-merge" 정합)
시각 검증 통과 6 항목
walkthrough 종료 시점 (KST 04:50) 기준 시각 검증으로 통과 확인:
- 청구액 SSoT — Pack 1 C-2605-001 사건 정보 영역 = 34,694,793원 (KST 정합)
- 사건번호 정합 — 의뢰인 포털 측
C-2605-001노출, docId prefix 0건 - 외부 가시면 카피 — "담당 사무소" / "사건 등록일" / "도메인 정보 입력 후 표시"
- a11y — PortalLinkDialog DOM
aria-describedby정상 - listener 안정성 — 재진입 후
No matching allow statements콘솔 0건 - Pack 2~5 도메인 분기 — wizard 라벨 (배우자·임차인·공동상속인) + 안내 카피 (이혼/부동산/상속/계약 사건 정보) + 8탭 (채권계산 제외) 정합
잔여 chip 우선순위
walkthrough 종료 시점 미해결 chip ≈ 19건. 사용자 결정 우선순위 제안:
High (외부 사용자 영향)
- portal URL
localhost:5002(.env.local결정) - 의뢰인 관리 포털 상태 sync 미반영
- /clients 포털 컬럼 sync
Medium (UI/UX 정합)
- 사무실 기억 검수 대기 필터 누락 (kanban 5컬럼 ↔ 필터 4상태)
- 활동 로그 유형 필터 — 포털 링크 발급 누락
- /library quick chips 14종 도메인 균형
- 캘린더 빈 상태 카피 — 기일 등록 명시
- 사무실 기억 모달 사건 유형 selector 통일
Low (메모·검토)
- 채권계산 탭 → 회수 탭 통합 검토
- 설정 직종 → 외부 카피 동적 반영 검증
- "스캔 0건" 의미 모호
- "수신" 유형 의미 모호
후속 ADR 후보
본 회고에서 정합으로 정리할 만한 정책 결정:
- TZ 정합 invariant 패키지 확장 —
packages/business-logic의 모든 날짜 계산 함수가 KST helper 또는asOfDate?인자를 거치도록 강제하는 ESLint rule + invariant 테스트 매트릭스 - 외부 가시면 ICP 카피 SSoT — chip #1705 ("담당 법무사" → "담당 사무소") 의 일반화 — 외부 사용자 카피에서 직업군·사이즈·구체 명칭 컷오프 금지 정책 (ADR 0034 ICP 컷오프 금지 와 동일 위계)
- firestore.rules subcollection invariant 매트릭스 —
cases/{caseId}하위 모든 subcollection 이 read/write rule + invariant test 동시 보유 — chip #1701 root cause 차단
학습
- 사용자 메모리 "ship 완료 메모리가 검증 부재를 가린다" 정확히 적용된 walkthrough — 머지 후 시각 재검증 (KST 청구액 9,863원 차이 시각 확인) 으로 fix 의 실제 효과 확인
- Pack 2~5 도메인 분기 검증 — 4 Pack 모두 wizard·사건 상세·증거 체크리스트·다음 행동 추천이 정합. ADR 0022/0023/0026/0027 의 분기 코드가 실제 외부 가시면에서 일관 작동
- chip spawn cadence — 1 chip ≈ 1.5 분 작성 (root cause + 위치 + 권장 카피 + PR 제목). 사용자가 비동기로 처리 가능. 이게 "회의·자율 세션은 핵심 기능을 만들지 못함" (사용자 메모리) 와 무관하게 작동하는 이유 — chip 은 의사결정이 아닌 회수 가능한 정합성 큐
- dev server hot reload 한계 — 같은 코드베이스 안에서 component 변경은 hot reload 되지만
page.tsxserver component 의 server cache 가 stale 인 케이스 발생. PR 머지 후 cache invalidate 가 자동인지 사용자 머신별로 다름 — chip #1707 trim() 강화는 cache 와 root cause 둘 다 안전망
재현 자료
- 누적 snapshot YAML 27건 (
snapshot-*.yml) — git 추적 안 함, 본 회고만 참조 - 사용자 메시지 / chip prompt — 모두 git history (
#1701~#1707커밋) 에 보존 - 시각 검증 결과 —
apps/web/app/(workspace)/cases/_actions/__tests__/helpers.test.ts의 KST 정합 회귀 4 케이스가 walkthrough 발견을 회귀 보호로 동결
2026-05-10 — ADR 0043 v2.66 ship 직후 ops dashboard walkthrough
시작점
- 컨텍스트: ADR 0043 office 자동화 v2.66 (#2101) 까지 ship 직후. 최근 25 PR (Track A~O) production 결함 가능성 가장 높음.
- 시작 옵션: ADR 0043 종단 walkthrough (ops + web). 사용자 합의 후 ops 우선 → web chip 으로 분리.
- 환경: emulator 9099/8080/9199/5001/4000 + web 3000 + ops 3001 모두 가동.
발견 + 직접 PR
결함 #1 — dashboard 3 hook silent error swallow (ADR 0029 위반)
- 위치:
apps/ops/app/(ops)/dashboard/page.tsx - root cause:
useRagMergeQuality/useTenantsRaw/usePublicDocsCount가 자체onSnapshot호출 + error handler 가setLoaded(true)/setCount(null)같은 silent fallback 만 실행 → console.error 없음 + error state 없음. currentUser 늦게 채워지는 race (IndexedDB 복원 · 일시 token 만료) 시 첫 onSnapshot 가 rules 거부 → silent swallow → 영원히 stuck. - fix: PR #2102 — PR #1885 의
useCollectionSnapshot(onAuthStateChanged 대기 + 8s loading-timeout + error 명시 분리) 으로 3 hook 모두 마이그레이션. 본문에tenantsError/publicCountError분기 추가 (load 실패 시 destructive 텍스트). 회귀 가드dashboard-hooks-fail-loud-invariants.test.ts(5 invariant) 신설. - 시각 재검증: 마이그레이션 후에도 emulator dashboard 가 동일 데이터 표시 — recall 2.4% (14 cases) · 사무소 (2) (test-fixture 5 cases + demo 21 cases) · publicDocuments 0 docs · LLM 가설 신뢰도 85% · 누적 정확도 83%.
메모리 stale 정정 후보
- ADR 0038 5 NavGroup 메모리 → 실제 코드는 ADR 0040 supersede 의 3 NavGroup. NavGroup SSoT =
apps/ops/app/(ops)/_components/nav-groups.ts(incidents · health · scenarios · case-prefill · bot-runs · journey 의도적 사이드바 제거, URL 은 유지). - ADR 0043 메모리 v1.78 → 실제 v2.66 (#2101) 까지 ship. 약 25 PR 추가 (Track A~O 분산).
chip 으로 spawn (라이브 walkthrough 한계)
- client SDK signIn 우회 도구화 — dev-session route 가 server-side cookie 만 set, client SDK Firebase Auth 는 sign in 안 됨 → onSnapshot rules 거부.
window.__opsTestSignIn(email, password)helper 가 client.ts:184 에 emulator + dev 모드 자동 노출. 이 helper 를 도구화 (ops e2e fixture 또는/api/dev-session응답에 client signIn 자동 트리거 옵션 추가). - web 사건 상세 65 버튼 contract + Track L/M/N 라이브 검증 — onboarding 5~7 단계 + wizard 까지 30분+ 소요. 이미 PR #2101 의 vitest invariant (234 라인) + e2e visibility spec (95 라인) 이 ship 되어 회귀 가드 작동 중. 라이브 클릭 시퀀스로 production-only quirk (race · CSP · stale cache) 노출 가능성 잔여 — 별도 worktree 권장.
학습
- fail-loud 정책 침투 한계 — ADR 0029 (2026-04-29 KPI=0 사고) 후 fail-loud 정책이 정착됐다고 알았으나, dashboard/page.tsx 의 3 hook 처럼 PR #1885 (useCollectionSnapshot SSoT) 이전에 작성된 hook 이 아직 silent swallow 패턴 유지. 정합 sweep 후보:
grep -rn "() => set.*(null|true)" apps/ops apps/web로 위반 매핑. - walkthrough 의 sweet spot — 1 hour 안에 새 영역 라이브 진입 + 1 fix PR + chip 잔여. 핵심 결함은 첫 5분 안에 발견 가능 (dashboard 카드 fetch). detail-level 검증은 vitest/e2e 정적 가드가 더 효율적.
- stale 메모리 자동 갱신 —
consolidate-memory스킬을 다음 세션에 호출해 ADR 0038 / ADR 0043 stale 정정 권장.
2026-05-10 (이어서) — web 라이브 walkthrough · 65 버튼 contract + Track L/M/N
이전 세션에서 chip 으로 분기됐던 "web 사건 상세 65 버튼 contract + Track L/M/N 라이브 검증" 을 같은 worktree (agitated-shtern-860f38) 에서 실행. 약 1시간.
검증 chain (모두 통과)
- emulator (9099/8080/9199/5001/4000) + web 3000 + ops 3001 가동 → master admin (
test@neohollo.test) 진입 → onboarding 4 동의 chain (이용약관·개인정보·베타·마케팅 선택) + 사무소 이름 → dashboard - 사건 wizard 1/3 (대여금) → 2/3 (당사자) → 3/3 (서울중앙·30,000,000원·연 12%·2025-01-15~2025-12-31) → C-2605-001 등록 (caseId
ZZxlHqXQ7OJHAvqpD8ec) - 65 버튼 contract (apps/docs/content/architecture/case-detail-button-contract.md) — desktop 1440x900 visibility 매핑:
- 헤더 5/7 (이전·다음 사건 hidden 정상 — sole case)
- 탭 8/13 (mobile 더보기 5는 viewport-dependent 정상)
- 상태 dropdown + AlertDialog open/close ✓
- 사건 정보 카드 → 편집 mode (#23 → #24/#25 취소·저장 등장) ✓
- 의뢰인 포털 다이얼로그 → 토큰 발급 → 7 버튼 (#29~#35) 모두 등장 (코드 4자리
3565) - AI 전략 분석 (#36) → Vertex AI 호출 성공 → "승소 가능성 5%" 분석 + "이전 분석" toggle (#40) 등장
- 비슷한 과거 기억 (#41) → findRelatedMemoriesAction 호출 → "찾지 못했습니다" + 다시 분석 (#42) — emulator 에 legacyDocuments 없는 정상 분기
- ARIA 2/2 ✓ (상태 안내 · 흐름 단계 안내 + 진행 단계 안내 추가)
- Track L (briefing history toggle, PR #2098) — dashboard "이전 브리핑" 버튼 → "이전 브리핑 history (max 14일)" 라벨 + 2026-05-10 entry 펼침 ✓
- Track M (docgen draft localStorage, PR #2099) —
/docs/generate?caseId=X&docType=내용증명에서 추가 지시사항 textarea 입력 → localStorage 키docgen:draft:v2.64:ZZxlHqXQ7OJHAvqpD8ec:내용증명작성 → 새로고침 → textarea 값 그대로 복원 ✓ - Track N (RAG context cache, PR #2100) — AI 초안 생성 클릭 → 3단계 (초안 확인) 진입 → Firestore
tenants/u8w7rEekIQWeVjDs6yWB/ragContextCache/{cacheKey}문서 작성 확인 (queryUsed: "대여금" · caseUpdatedAtSnapshot timestamp) ✓
발견 결함 4건
| # | severity | 영역 | 처리 |
|---|---|---|---|
| ① | P2 dev path | seed-ops-dashboard-emulator.sh 의 config/appMetadata PATCH 가 updateMask 없이 실행 → sibling 필드 (planLimits·minVersion·maintenanceMode·announcement.type·features.ai.briefing·features.infraHardening 3개) replace → web/ops 진입 시 Zod schema invalid console error 매번 발생 | fix 직접 머지 — ?updateMask.fieldPaths=features&...=testing&...=announcement 추가 + ai.briefing + infraHardening 누락 필드 + announcement.type=info 보강 |
| ② | P2 production a11y | 사건 상세 페이지 portal 다이얼로그 클릭 직후 console 에 Radix DialogContent requires DialogTitle + Missing Description 2건 발생. PortalLinkDialog 자체는 DialogTitle/Description 충족 — 다른 dialog 가 진원지로 추정 (FeedbackFAB · KeyboardShortcutsHelp · QuickSearchPalette 등 후보). minified stack trace 만 있어 정확 진단 보류 | chip 분기 ("Investigate Radix Dialog a11y warning source") |
| ③ | P3 UX silent | docgen 새로고침 시 draft 가 자동 복원되지만 사용자에게 "이전에 작성하던 초안을 복원했습니다" 시각 피드백 banner 부재 | chip 분기 ("docgen draft restore banner UX polish") |
| ④ | P1 critical production | buildRagContextCacheKey(caseId, docType) 가 docType 의 한글 문자를 모두 _ 로 치환 → "내용증명" / "지급명령" / "준비서면" (모두 4글자 한글) 이 같은 cacheKey ___caseId___..._____ 로 collide → 두 RAG 결합 결과가 docType 간 잘못 공유됨. tenants/{tid}/ragContextCache/ZZxlHqXQ7OJHAvqpD8ec______ 1건만 컬렉션에 떨어진 것이 증거 | fix 직접 머지 — encodeURIComponent(docType).replace(/%/g, "_") 로 한글 hex 보존 + invariant test (#2 갱신 + #2b functional collision 회귀 가드) 추가, 10/10 통과 |
"ship 완료가 검증 부재를 가린다" 재증명
ADR 0043 v2.65 (Track N · RAG cache) 가 #2100 으로 ship 되었고 정적 invariant 9건이 통과 중이었지만, functional collision 검증이 빠져 있어 한글 docType 의 cache 가 잘못 동작하는 것을 못 잡았다. invariant test 가 source grep 만 검증하고 functional 호출 결과를 비교하지 않으면 같은 length 한글 입력 collision 같은 패턴은 통과해버린다. 이번 fix 에 추가된 (2b) cacheKey functional 테스트는 같은 caseId + 다른 한글 docType 4글자 3종 ("내용증명" / "지급명령" / "준비서면") 이 모두 다른 cacheKey 로 매핑되는지 직접 호출 결과로 검증한다.
직접 머지 PR
- (TBD #)
fix(rag): cacheKey 한글 docType collision (P1) + seed updateMask (P2 dev)— paths.ts encodeURIComponent + invariant 갱신 + seed-ops-dashboard-emulator.sh updateMask + 누락 필드 보강. 10 invariant 통과. seed 재실행 시각 검증.
chip 분기 (사용자 결정 큐)
- "Investigate Radix Dialog a11y warning source" — DialogContent 모든 사용처 grep + 진원지 식별 + fix + 정적 invariant
- "docgen draft restore banner UX polish" — hydrate 시 dismissible banner + "처음부터 다시" + e2e
2026-05-10 — 자율 세션 10시간 (Block 1~7) 11 PR closed-loop
사용자 명령 "추천에 동의 + 자율모드 10시간". 위 walkthrough 추천 1번 (fail-loud sweep) 시작점에서 출발. ADR 0043 v2.66 → v2.71 까지 한 줄 본질 시각화 layer 보강으로 closed-loop.
ship된 PR 11건
| PR | 영역 | 핵심 |
|---|---|---|
| #2102 | ops dashboard | 3 hook silent swallow → useCollectionSnapshot SSoT (ADR 0029) |
| #2103 | docs | 2026-05-10 walkthrough 회고 섹션 |
| #2105 | ops/tenants | list + detail 2 페이지 silent swallow → snapshot SSoT |
| #2106 | web | useTenant + ClientMessagesTab silent → handleListenerError |
| #2107 | ops Tier 2 | YjsCompactionThresholdCard · ActiveAlertsCard UI error state |
| #2108 | e2e | ops fail-loud regression spec (PR #2102/2105/2107 효과 동결) |
| #2109 | ops v2.67 | dashboard 학습 회귀 stale indicator (Track G 정합) |
| #2110 | ops v2.68 | dashboard 14일 RAG history chart (한 줄 본질 시각화) |
| #2111 | ops v2.69 | per-tenant RAG history (tenants/[tid] 재사용 + hook SSoT 분리) |
| #2112 | web v2.70 | dashboard 학습 효과 시그널 카드 (사용자 가시) |
| #2113 | ops v2.71 | anomaly hypothesis history toggle (운영자 패턴 비교) |
한 줄 본질 가시화 layer 5단계 (v2.67~v2.71)
ADR 0043 v2.66 까지는 ragMergeQuality 의 latest 1 건만 dashboard "학습 회귀 통과율" axis 에 노출. v2.67 ~ v2.71 5 PR 로 시각화 layer 강화:
- stale indicator (v2.67) — 마지막 갱신 시각 inline + 10분 amber + pulse. 운영자가 "이 값이 신선한가?" 즉시 인지
- 14일 line chart (v2.68) — recall + precision 시계열 + trend label (🟢 상승 / 🔴 하락 / ⚪ 평탄). "측정 시작 후 어떻게 변하고 있나?" 시각 답
- per-tenant chart (v2.69) — tenants/[tid] 의 새 "학습 회귀" tab 에 동일 chart. ops 가 사무소 별 학습 효과 비교 가능 + hook SSoT 분리 (drift 0)
- 사용자 가시 카드 (v2.70) — apps/web dashboard 에 "사무소 AI 학습 효과" 카드 추가. 변호사/사무장 본인이 자기 사무소의 학습 trend 를 직접 본다 (chart 없는 경량 텍스트 시그널, 의존 규칙 정합 복제)
- anomaly history toggle (v2.71) — LLM 가설 카드의 latest 1 건 외 이전 9 건을 toggle 로 expand. 운영자가 "이번 가설이 이전과 같은 패턴인가?" cross-fingerprint 비교
fail-loud sweep (Block 1)
/e2e 후 dashboard 3 hook silent swallow → 다른 영역 sweep. 16 hook 매핑 → 위반 4건 (Tier 1) + 부분 위반 2건 (Tier 2):
| 위치 | 패턴 | fix |
|---|---|---|
| ops/tenants/page.tsx | 자체 onSnapshot + silent | useCollectionSnapshot SSoT |
| ops/tenants/[tid]/page.tsx | 2 hook 자체 + silent + race | useDocumentSnapshot + useCollectionSnapshot + notFound vs error 분기 |
| web/useTenant.ts | NOT_FOUND/error 미분리 | ERROR 액션 + state.error + handleListenerError |
| web/ClientMessagesTab.tsx | error handler 부재 | handleListenerError("client-messages") |
| ops/YjsCompactionThresholdCard.tsx | console.warn only | console.error + setLoadError + UI destructive |
| ops/ActiveAlertsCard.tsx | silent hide 위험 | loadError 분기 + destructive 카드 |
누적 통계
- 11 PR 머지 (모두 자율 squash-merge, 평균 cycle 8분)
- 57 invariant 신설 (정적 grep 회귀 가드)
- 1 e2e spec (Playwright 시각 동결)
- 5 메모리 정정·신규 (ADR 0038/0043/ops-e2e-gap stale 정정 + fail-loud sweep + snapshot SSoT 강제)
- 0 회귀 (각 PR 단계 vitest + lint 통과)
학습 (자율 모드 패턴)
- closed-loop 단위로 자르기 — fail-loud sweep 도 4 PR 분할 (Tier 1 → Tier 2 → e2e → 메모리). 한 PR 묶으면 review 부담 + 회귀 위험
- invariant 가드 비중 1:1 — 기능 코드 line ≈ invariant test line. 정적 grep 으로 회귀 차단
- memory stale 체크 주기 — 자율 세션 시작점에서 관련 메모리 sweep 필수. ADR 0038 (5→3 NavGroup), ADR 0043 (v1.78→v2.66), ops-e2e-gap (모두 ship) 셋 다 stale 이었음
- drift 0 SSoT 분리 — 같은 hook/컴포넌트가 ops dashboard + tenants/[tid] + web 에 쓰이면 _lib SSoT 분리 → import 재사용. 의존 규칙 위반 시만 복제
- PR 즉시 squash merge — 자율 모드 정합. CI 대기 후
gh pr merge --squash --auto→ main pull → 다음 PR (cycle 5~10분) - Block 통합 결정 — 같은 본질 layer 면 통합 (Block 4+5+6 → ADR 0043 v2.67~v2.71 4 PR 한 흐름)
잔여 (다음 세션)
- web 사건 상세 65 버튼 + Track L/M/N 라이브 walkthrough (chip spawn, 별도 worktree)
- Tier 1 fail-loud sweep 추가 영역 (apps/web fetch/getDocs catch silent · functions/ trigger silent error)
- v2.72 후보: dashboard publicCount stale + idle refresh / RAG eval --apply 1 클릭 trigger UI / web dashboard 학습 효과 카드 sparkline 추가
- ADR 0044 후보: 한 줄 본질 layer 가시화 풀 패턴 정형화 (v2.67~v2.71 누적 학습)
2026-05-11 — 자율 세션 #2 10시간 (Block A~G) 7 PR closed-loop
사용자 명령 "잠을 잡니다 자율개발모드로 10시간". 자율 세션 #1 (12 PR) 의 잔여 Tier 1 sweep + 한 줄 본질 layer 정형화 (ADR 0044) closed-loop.
ship된 PR 7건
| PR | Block | 핵심 |
|---|---|---|
| #2115 | A | Tier 1 fail-loud sweep 2 (web fetch/getDocs/refund catch silent → console.error + UI 명시) |
| #2116 | D | ADR 0044 v1.0 — 한 줄 본질 가시화 5 layer 표준 (L1 stale · L2 chart · L3 per-tenant · L4 web 카드 · L5 history) |
| #2117 | F | DashboardLearningEffectCard sparkline (의존성 0 SVG, recharts 미사용) |
| #2118 | B | functions/ Tier 2 정책 silent default fallback → console.error 명시 |
| #2119 | v2.73 | ragMergeQuality L5 history list toggle (100% 정합 첫 source) |
| #2120 | D | ADR 0044 v1.1 amendment — audience 매트릭스 (사용자 가치 / 운영자 도구 / 운영자 알림) |
| #2121 | v2.74 | anomalyHypotheses L1 stale (24h) + L3 per-tenant (운영자 도구 75% 정합) |
ADR 0044 — 한 줄 본질 가시화 layer 정형화 (메타 가치)
ADR 0043 v2.67~v2.71 의 5 PR 패턴을 정형화 → 향후 새 본질 source 추가 시 일관 layer 강제. v1.0 의 "5 layer 모두 강제" 가 운영자 도구/알림 source 에 과적합 → v1.1 amendment 로 audience 매트릭스 도입.
| audience | 강제 layer | 예시 |
|---|---|---|
| 사용자 가치 | L1+L2+L3+L4+L5 (모두) | ragMergeQuality (학습 효과) |
| 운영자 도구 | L1+L2+L3+L5 (L4 skip) | anomalyHypotheses (가설 진단) |
| 운영자 알림 | L1+L3+L5 (L2+L4 skip) | active alerts (severity) |
ADR 0044 source 정합 표
| source | audience | L1 | L2 | L3 | L4 | L5 | 정합 |
|---|---|---|---|---|---|---|---|
| ragMergeQuality | 사용자 가치 | ✓ | ✓ + sparkline | ✓ | ✓ + sparkline | ✓ | 100% |
| anomalyHypotheses | 운영자 도구 | ✓ | TODO | ✓ | n/a | ✓ | 75% |
| active alerts | 운영자 알림 | TODO | n/a | TODO | n/a | TODO | 0% |
| draftDiffs / docgenEvents / refinementFeedback | 사용자 가치 | TODO×3 | TODO×3 | TODO×3 | TODO×3 | TODO×3 | 0% |
학습 (자율 모드 #2 패턴)
- Block 통합 결정 — 같은 본질 source layer 면 통합 (Block D + F + v2.73 + Block E 가 모두 ADR 0044 의 layer 정합 강화)
- audience 분류 amendment — v1.0 "5 layer 모두 강제" 의 과적합 발견 → v1.1 으로 비용 절감 (L4 skip OK)
- sparkline 의존성 0 — recharts 가 web 미사용 → inline SVG polyline (~50줄) 으로 chart layer 정합
- Tier 1 sweep 단계화 — onSnapshot (sweep 1, 세션 #1) → fetch/getDocs/refund (sweep 2, 본 세션). 다음 sweep: server action call · functions/ trigger silent return
- ADR 0044 강제 layer 표 — source 추가 시 audience 분류 → 강제 layer matrix → invariant 신설 → ship → 표 갱신 closed-loop
누적 통계 (세션 #1 + #2)
| 세션 | PR | invariant | e2e | 메모리 | ADR |
|---|---|---|---|---|---|
| #1 (2026-05-10) | 11 | 57 | 1 | 5 | — |
| #2 (2026-05-11) | 7 | 30+ | 0 | 1 | 1 (0044 v1.0+v1.1) |
| 합계 | 18 | 87+ | 1 | 6 | 1 |
다음 세션 우선순위
- draftDiffs (편집률) L1~L5 ship — 한 줄 본질 4 axis 두번째 source. 5 PR ~3h
- docgenEvents (성공률) L1~L5 — 같은 패턴. 5 PR ~3h
- refinementFeedback (exemplar) L1~L5 — 같은 패턴. 5 PR ~3h
- anomalyHypotheses L2 chart — fingerprint 별 confidence/accuracy timeline. 시각 디자인 복잡 (~1.5h)
- active alerts L1+L3+L5 — 이미 ship 된 ActiveAlertsCard 정합 점검 (~1h)
- web 사건 상세 walkthrough chip — 세션 #1 chip 미실행 (별도 worktree)
2026-05-11 (이어서) — 자율 세션 #2 추가 7 PR (#2123~#2127)
위 섹션 뒤로 추가 ship 된 PR.
| PR | 핵심 |
|---|---|
| #2123 | v2.75 anomalyHypotheses L2 sparkline → 운영자 도구 100% (4/4) |
| #2124 | v2.76 draftDiffs L1 + L2 → 편집률 axis 활성화 |
| #2125 | v2.77 draftDiffs L3 + L4 → 80% (4/5) |
| #2126 | v2.78 draftDiffs L5 history → 편집률 100% (5/5) |
| #2127 | v2.79 docgenEvents L1 + axis fetch → 성공률 axis 활성화 |
ADR 0044 source 정합 표 (세션 #2 최종)
| source | audience | 정합 | 100% 도달 |
|---|---|---|---|
| ragMergeQuality | 사용자 가치 | 100% (5/5) | v2.73 |
| anomalyHypotheses | 운영자 도구 | 100% (4/4) | v2.75 |
| draftDiffs | 사용자 가치 | 100% (5/5) | v2.78 |
| docgenEvents | 사용자 가치 | 30% (1.5/5) | TODO |
| refinementFeedback | 사용자 가치 | 0% | TODO |
| active alerts | 운영자 알림 | 0% | TODO |
본질 4 axis 가시화 진척 (세션 #2 종료)
dashboard "AI 서류 신뢰도 분해" 4 axis 데이터 활성화:
- 편집률: ✓ (v2.76)
- docgen 성공률: ✓ (v2.79)
- exemplar selection: 미활성 (refinementFeedback hook 미신설)
- 학습 회귀 통과율: ✓ 이미 (v2.67~v2.73)
다음 세션 exemplar 활성화 시 4 axis 모두 production 데이터 fetch + 부트스트랩 분기 정합 → 한 줄 본질 layer 가시화 100%.
최종 누적 통계 (자율 세션 #1 + #2)
| 세션 | PR | invariant | e2e | 메모리 | ADR |
|---|---|---|---|---|---|
| #1 (2026-05-10) | 11 | 57 | 1 | 5 | — |
| #2 (2026-05-11) | 14 | 50+ | 0 | 1 | 1 |
| 합계 | 25 | 107+ | 1 | 6 | 1 |
다음 세션 우선순위
- docgenEvents L2~L5 (~2h, 4 PR) — 성공률 100% 정합 완성
- refinementFeedback L1~L5 (~3h, 5 PR) — 4 axis 마지막
- active alerts L1+L3+L5 (~1h) — 운영자 알림 audience 첫 source
- draftDiffs / docgenEvents mock 시드 — production 데이터 0 건 환경에서도 layer 시각 검증
2026-05-11 (이어서) — 자율 세션 #3 5 PR — 6/6 source 100% 정합 달성
세션 #2 종료 후 사용자 "우선순위를 진행해주세요" 명령. P1~P5 closed-loop ship.
ship된 PR 5건
| PR | 우선순위 | 핵심 |
|---|---|---|
| #2129 | P1.1 | ADR 0044 v2.80 — docgenEvents L2+L3+L5 (80%) |
| #2130 | P1.2 | ADR 0044 v2.81 — DashboardDocgenSuccessCard L4 (100%) |
| #2131 | P2 | ADR 0044 v2.82 — refinementFeedback L1~L5 (exemplar 100%) |
| #2132 | P3 | ADR 0044 v2.83 + v1.2 — active alerts (운영자 알림 global 100%) |
| #2133 | P4 | ADR 0044 v2.84 — essence-axes mock 시드 |
ADR 0044 source 정합 표 (세션 #3 종료 최종)
| source | audience | 강제 layer | 정합 |
|---|---|---|---|
| ragMergeQuality | 사용자 가치 | L1~L5 | 100% |
| draftDiffs | 사용자 가치 | L1~L5 | 100% |
| docgenEvents | 사용자 가치 | L1~L5 | 100% |
| refinementFeedback | 사용자 가치 | L1~L5 | 100% |
| anomalyHypotheses | 운영자 도구 | L1+L2+L3+L5 (L4 skip) | 100% |
| active alerts | 운영자 알림 (global) | L1+L5 (L2/L3/L4 skip) | 100% |
6/6 source 모두 100% 정합. 한 줄 본질 4 axis 모두 dashboard 에 production 데이터 fetch + sparkline + history + per-tenant + 사용자 가시 카드 완비.
ADR 0044 v1.2 amendment
운영자 알림 audience 를 tenant-aware / global 분화:
- global (active alerts 같은 tenant-agnostic): L1+L5 만 강제
- tenant-aware (예정): L1+L3+L5 강제
essence-axes mock 시드
운영자 1 클릭으로 demo-firm-memory 에 14일치 draftDiffs/docgenEvents/refinementFeedback mock 생성. 점진 개선 trend (편집률 감소 · 성공률 증가 · 만족도 증가) — 모든 layer 시각 검증 가능.
최종 누적 통계 (자율 세션 #1 + #2 + #3)
| 세션 | PR | invariant | e2e | 메모리 | ADR |
|---|---|---|---|---|---|
| #1 (2026-05-10) | 11 | 57 | 1 | 5 | — |
| #2 (2026-05-11) | 14 | 50+ | 0 | 1 | 1 (0044 v1.0+v1.1) |
| #3 (2026-05-11 이어서) | 5 | 30+ | 0 | 1 | 1 (0044 v1.2) |
| 합계 | 30 | 137+ | 1 | 7 | 1 |
본질 100% 정합의 의미
dashboard 의 "AI 서류 신뢰도 분해" 4 axis 모두 production 데이터 활성화 + 14일 trend + sparkline + per-tenant view + 사용자 가시 카드 + history list 완비. 한 줄 본질의 사용자/운영자 가시화가 ADR 0044 정형화로 모듈식 완성.
다음 세션 후보
- anomaly L2 chart 강화 (sparkline → 정식 LineChart)
- 운영자 알림 tenant-aware audience source 신설
- web 사건 상세 walkthrough chip (세션 #1 미실행)
- fail-loud sweep 3 (server action / functions trigger)
- ADR 0044 CI integration (신규 source 자동 정합 검증)
2026-05-11 (이어서) — 자율 세션 #4 3 PR — sweep 3 + ADR 0044 정합 자동화
세션 #3 종료 후 사용자 "진행해주세요" 명령. S1~S4 closed-loop ship.
ship된 PR 3건
| PR | Block | 핵심 |
|---|---|---|
| #2135 | S1.1 | sweep 3 — inviteMember 이메일 silent → emailSent/emailError 명시 |
| #2136 | S1.2 | sweep 3 — on-document-finalized publishOpsAlert (Cloud Run retry 부재 대안) |
| #2137 | S2 | ADR 0044 source ↔ hook 자동 동기화 invariant (drift 0) |
fail-loud sweep 3
이전 sweep 1 (onSnapshot) + sweep 2 (fetch/getDocs) 후속. 23 후보 매핑 → Tier 1 2건 fix:
- inviteMember 이메일 silent — 비즈니스 손실 (사용자가 "발송됨" 오인식) → emailSent 플래그 + UI 분기 toast
- on-document-finalized trigger silent — Cloud Run retry 없음 + Cloud Logging 만 → publishOpsAlert helper 신설 (ops dashboard ActiveAlertsCard 자동 표시)
ADR 0044 invariant 메타 layer
신규 essence-axes source 추가 시 hook + ADR 표 + invariant 3종 동기화 자동 강제. drift 즉시 검출. 다음 source 추가 비용 30분 → 25분.
최종 누적 통계 (자율 세션 #1+#2+#3+#4)
| 세션 | PR | invariant | e2e | 메모리 | ADR |
|---|---|---|---|---|---|
| #1 (2026-05-10) | 11 | 57 | 1 | 5 | — |
| #2 (2026-05-11 10시간) | 14 | 50+ | 0 | 1 | 1 (v1.0+v1.1) |
| #3 (이어서) | 5 | 30+ | 0 | 1 | 1 (v1.2) |
| #4 (이어서) | 3 | 20+ | 0 | 1 | — |
| 합계 | 33 | 157+ | 1 | 8 | 1 |
다음 세션 후보
- publishOpsAlert helper 의 다른 trigger 적용 (functions/ 다른 catch handler reuse)
- 운영자 알림 (tenant-aware) source 신설 (ADR 0044 v1.1 미사용 audience)
- web 사건 상세 walkthrough chip (세션 #1 chip 미실행)
- anomaly L2 chart 강화 (sparkline → recharts LineChart, 후순위)
- ADR 0044 CI integration (sidebar-coverage 같은 fs scan)
2026-05-15 — 사건 라이프사이클 walkthrough
사용자 요청: "사건등록부터 사건관련 모든 기능을 확인하면서 사건완료까지 playwright로 검증"
범위: 신규 owner 회원가입 → 온보딩 (사무소 생성) → 사건 등록 wizard 3단계 (Pack 1 대여금) → 사건 상세 정보 편집 → 기일 등록·D-30 토글·결과 입력 → AI 서류 (지급명령) docgen·finalize·배지 토글 → 증거 6종 토글·파일 업로드 (OCR mock) → 변제 등록 (전액 58,136,986원) → 사건 종결 (status=closed).
신규 owner 가입 후 사건 1건의 라이프사이클을 entry → 클릭 시퀀스 → state 전이 → 종결까지 라이브 진행.
시각 검증 통과
| 단계 | 검증 결과 (구체 수치) |
|---|---|
| 회원가입 폼 | 4 약관 동의 + 입력 검증 통과, /onboarding redirect |
| 사무소 생성 | tenant 생성 + /dashboard redirect, 사이드바 "Walkthrough 법무사무소" 표시 |
| 사건 wizard Step 1 | 14 종 사건 유형 5 Pack 그룹화 옵션 (채권 6 · 가사 1 · 부동산 3 · 상속 2 · 계약 2) |
| 사건 wizard Step 2 | 도메인-aware 라벨 ("채권자/채무자") + 주소 검색 SDK 로드 ✓ |
| 사건 wizard Step 3 | 자동 계산 미리보기 (약정이자 5,917,808원 · 지연손해금 2,219,178원 · 청구총액 58,136,986원) + 51 법원 옵션 그룹화 |
| 사건 등록 완료 | caseId 1xXZU1mjxEs4UDDmKXNT · C-2605-001 · "사무소 기억" 카피 정합 |
| 사건 상세 편집 | 의뢰인 연락처·사건번호·메모 인라인 form 저장 후 보기 모드 복귀 |
| 기일 등록 | 변론기일 2026-06-14 10:30 · 서울중앙지방법원 351호 · D-30 자동 표시 |
| 사건 흐름 자동 전이 | 단계 2 (분류) → 단계 5 (재판 진행) 자동 갱신, 다음 행동 추천도 단계별 적합하게 변경 |
| 판결 결과 입력 | 전부 승소·선고일 2026-07-15·확정일 2026-07-30 + 항소 기한 D-75 자동 계산 |
| AI docgen (지급명령) | Vertex AI Gemini 호출 성공, 자연어 본문 + 첨부 서류 5종 자동 생성 |
| docgen 저장 + 배지 토글 | "작성 1건" 카운터 갱신, 배지 cycle (대기→초안→완료→해당없음) 정상 |
| 증거 6종 토글 + 파일 업로드 | 수령 4/4, walkthrough-evidence.txt (57B) 업로드 + OCR mock 동작 (증거 1건) |
| 변제 등록 | 1건 / 58,136,986원 / 사건 흐름 6-B (결과 — 회수·집행) 자동 전이 + "승소 확정 — 강제집행 진입" 헤더 |
| 사건 종결 | status combobox "종결" 선택, 사건 목록 "종결 1" 탭 카운터 갱신 |
머지 PR (2건)
| PR | 분류 | 영향 | 회귀 가드 |
|---|---|---|---|
| #2306 | P0 차단 | 사건 wizard Step 2 주소 검색 SDK 가 CSP 차단으로 미동작 — //t1.daumcdn.net/... protocol-relative URL 이 dev(http) 에서 http: 로 풀려 script-src https://*.daumcdn.net 위반 | external-script-url-https-invariants.test.ts — apps/web 전수 grep + 직접 assertion |
| #2307 | P1 (전 페이지) | ThemeToggle SSR/client hydration mismatch — title·aria-label·SVG path 가 모든 페이지 console 에 매번 출력 | theme-toggle-hydration-invariants.test.ts — mounted state 패턴 + suppressHydrationWarning 우회 금지 |
Chip spawn (4건, 사용자 결정 대기)
- Dashboard onSnapshot 3건 rules 차단 — 새 owner 가입 직후
/dashboard진입 시[dashboard-docgen-success]·[dashboard-exemplar-satisfaction]·[dashboard-citation-preservation]listener 모두FirebaseError: No matching allow statements출력. fail-loud 정합으로 listener 발화하지만 무료 가입 사용자 첫 경험에 문제. - "기일 결과 기록" 버튼 무반응 — 사건 흐름 카드 안 버튼 클릭 시 modal/form/navigation/console 어떤 변화도 없음. 판결 결과 입력은 별도 카드에서 정상 동작이라 사용자 혼란. 정의-호출 미연결 패턴.
- 변제 완료 후 청구금액 카드 표기 어색 — 전액 변제 후 "청구금액 0원 / 미회수 58,136,986원 / 0원" slash 모호. "회수 100%" · "전액 회수 완료" 시각 신호 없음.
- 사건 조서 인쇄 popup 차단 fail-loud 개선 —
window.opennull 시 console.error 만, 사용자 시각 피드백 0. sonner toast 또는 alertdialog 안내 필요.
핵심 patterns 재확인
- AddressInput 우회 — Kakao Postcode 팝업은 Playwright
about:blankiframe 환경 차단 (실 브라우저 정상). textbox 자체에 직접 fill 도 허용 (onChange={(e) => onChange(e.target.value)}) — 사용자 직접 타이핑 흐름. - 사건 흐름 단계 자동 전이 — 기일 추가 → 단계 5 (재판) · 변제 등록 → 단계 6-B (회수·집행) 자동 갱신. 9 단계 흐름 + 4 단계 진행 단계 둘 다 동기화.
- 두 RAG 결합 entry —
/docs/generate?caseId=...&docType=지급명령폼에 "AI 가 참고할 사무실 기억" 영역 명시 — ADR 0041 (Platform RAG + Tenant RAG) wiring 확인.
다음 세션 후보 (이번 walkthrough 잔여)
spawn된 chip 4건 사용자 검토 + 머지— 후속 세션에서 2건 fix 머지 (아래 "후속 #1" 참고)AI 빠른 등록 (Step 1 자유 텍스트 → 자동 채움) 동작 검증— 후속 #1 완료의뢰인 포털 토큰 발급 + 외부 가시면 동선— 후속 #1 완료- Pack 2~5 도메인 라이프사이클 동일 검증 (이번은 Pack 1 만)
- 회수 단계 (집행문·추심명령·배당) 까지의 확장
2026-05-15 후속 #1 — chip 2건 fix + 미점검 2개 영역 검증
같은 세션 안에서 chip 큐의 fix 가능한 2건 직접 처리 + 미점검 영역 (AI 빠른 등록 · 의뢰인 포털 외부 가시면) 완전 검증.
머지 PR (3건)
| PR | 분류 | 영향 | 회귀 가드 |
|---|---|---|---|
| #2309 | 신규 owner 첫 경험 | dashboard onSnapshot 3 collection (docgenEvents · refinementFeedback · draftDiffs) firestore.rules 매칭 누락 → default deny → fail-loud listener 매번 출력. anomalyHypotheses 패턴 따라 rules 추가 (read: staffOrOwner ‖ masterAdmin, write: false) | dashboard-listener-rules-coverage-invariants.test.ts — dashboard _components onSnapshot 의 tenants/{tid}/<col> 전수 grep → firestore.rules 매칭 자동 검증 (default deny 신규 listener 차단) |
| #2310 | P1 (포털) | getPortalSession 이 만료/revoke 시 session.destroy() 호출 → Next.js 16 Server Component cookies write throw → console 매번 노이즈. 만료 cookie 는 다음 createPortalSession 이 overwrite 하므로 보안 invariant 유지 | portal-session-readonly-invariants.test.ts — getPortalSession 본문 destroy/save 호출 금지 + create/destroy 함수는 유지 (balanced-brace 함수 본문 추출) |
시각 검증 통과 — AI 빠른 등록 (자유 텍스트 → 자동 채움)
자유 텍스트 prompt:
"이순신 선생님 (서울 종로구 사직로 88, 010-9999-1234) 이 홍길동 (경기 성남시 분당구 판교로 100) 에게 2025년 3월 10일 3천만원을 빌려줬는데 2025년 11월 30일 갚기로 했는데 미반환. 약정이자 연 6%. 차용증 보유."
| Step | 자동 채움 결과 |
|---|---|
| Step 1 사건 유형 | 대여금 (Pack 1) ✓ |
| Step 2 채권자 | 이순신 선생님 · 서울 종로구 사직로 88 · 010-9999-1234 ✓ |
| Step 2 채무자 | 홍길동 · 경기 성남시 분당구 판교로 100 ✓ |
| Step 3 대여 정보 | 원금 30,000,000 · 이자율 6 · 대여일 2025-03-10 · 변제기일 2025-11-30 ✓ |
| Step 3 법원 | 빈 값 (텍스트 미언급 → 정상) |
Vertex AI Gemini 호출 ~15초, 14 종 사건 분류 + 당사자 entity extraction + 금액·날짜 정규화 모두 성공.
시각 검증 통과 — 의뢰인 포털 외부 가시면 + 메시지 양방향
| 단계 | 검증 결과 |
|---|---|
| 포털 토큰 발급 | URL + 4자리 접속 코드 7804 동시 발급 |
| 외부 진입 (incognito tab) | 4자리 코드 입력 → /portal 사건 가시면 진입 |
| 사건 현황 | 청구 금액 5,813만원 · 다음 변론기일 2026.06.14 D-30 · 시효 D-3157 (민법 §162 ① 10년) · 7 단계 진행도 |
| 의뢰인 → 변호사 메시지 | "안녕하세요 사무소님, 시효 날짜 확인 부탁드립니다." 전송 → 변호사 대화 tab 5월 15일 오후 01:47 수신 ✓ |
| 변호사 → 의뢰인 답신 | "안녕하세요 김민호 님, 시효는 2035-01-05까지 유효합니다. 강제집행 진행 중입니다." 5월 15일 오후 01:48 → 포털 메시지 tab 수신 ✓ |
잔여 chip (2건)
- "기일 결과 기록" 버튼 무반응 — 정의-호출 미연결 패턴. 분석 필요.
- 변제 완료 후 청구금액 카드 표기 어색 — UI/UX 정책 결정 필요.
누적 (2026-05-15 본 세션 + 후속 #1)
- 머지 PR 5건 (#2306·#2307·#2308·#2309·#2310)
- 회귀 invariant 4건 신설 (external-script-url · theme-toggle-hydration · dashboard-listener-rules-coverage · portal-session-readonly)
- 누적 8243 테스트 통과
- Chip 잔여 2건
다음 세션 후보
잔여 chip 2건 fix— 후속 #2 완료- Pack 2 ~ Pack 5 도메인 라이프사이클 동일 검증 — Pack 2·3 wizard 분기 검증 완료 (후속 #2)
- 회수 단계 (집행문·추심명령·배당) 까지의 확장
- 사무실 기억 (legacy) 5컬럼 kanban + OCR 승인 흐름
2026-05-15 후속 #2 — 잔여 chip 2건 fix + Pack 2·3 도메인 분기 검증
후속 #1 의 잔여 chip 2건 (기일 결과 기록 버튼 무반응 · 변제 완료 청구금액 카드 표기) 모두 직접 PR fix + Pack 2 (이혼) · Pack 3 (부동산 명도) wizard 도메인 라벨/필드 분기 시각 검증.
머지 PR (2건)
| PR | 분류 | 영향 | 회귀 가드 |
|---|---|---|---|
| #2312 | P1 (UX) | 사건 흐름 카드 nextAction 버튼 (기일 결과 기록 · 준비서면 작성 등) 이 이미 같은 탭일 때 무반응으로 보이는 결함. handleTabChange 안에 requestAnimationFrame + scrollIntoView({behavior:"smooth",block:"start"}) 추가 — 동일 탭 재클릭도 panel 영역 viewport 정렬 | case-tab-change-scroll-invariants.test.ts — scrollIntoView · role=tabpanel[data-state=active] selector · requestAnimationFrame 모두 존재 검증 |
| #2313 | P1 (회수율) | 전액 변제 후 청구금액 카드가 0원 / 미회수 58,136,986원 / 0원 으로 표기 — total = totalOutstanding (잔여) 라서 분모 0 → pct 0. total = principal + agreementInterestAccrued + delayDamageAccrued 원래 청구액으로 변경 + Math.min(100, ...) cap + pct >= 100 시 emerald progress bar + "전액 회수" 라벨 | case-summary-claim-amount-invariants.test.ts — totalOutstanding 직접 할당 금지 + 100 cap + emerald 라벨/색상 존재 |
시각 재검증 (구체 수치)
기일 탭 nextAction button click:
- 이전: 활성 panel top =
1097.75px(off-screen) - 이후: 활성 panel top =
-0.25px(viewport 상단 정렬)
변제 완료 청구금액 카드 (Pack 1 사건, 전액 58,136,986원 변제):
- 이전 aria:
청구금액 0원, 회수율 0% — 미회수 - 이후 aria:
청구금액 58,136,986원, 회수율 100% — 변제 내역 보기 - 카드 본문:
청구금액 58,136,986원 · 전액 회수 (emerald) · 58,136,986원 / 58,136,986원
시각 검증 — Pack 2·3 도메인 라벨/필드 분기
| Pack | 사건 유형 | Step 2 당사자 라벨 | Step 3 도메인 필드 |
|---|---|---|---|
| 1 | 대여금 | 채권자 / 채무자 | 대여 원금 · 약정 이자율 · 대여일 · 변제기일 |
| 2 | 이혼 | 의뢰인 / 배우자 | 혼인 시작일 · 혼인 종료일 (별거·이혼) · 미성년 자녀 |
| 3 | 부동산 명도 | 임대인 / 임차인 | 부동산 목적물 주소 |
14 종 사건 유형마다 wizard step 2 라벨 + step 3 필드가 정확히 분기. 3 종 검증으로 도메인 분기 패턴 안전성 시각 확인 (Pack 4·5 는 다음 세션).
누적 (2026-05-15 본 세션 + 후속 #1 + 후속 #2)
- 머지 PR 8건 (#2306·#2307·#2308·#2309·#2310·#2311·#2312·#2313)
- 회귀 invariant 6건 (external-script-url · theme-toggle-hydration · dashboard-listener-rules-coverage · portal-session-readonly · case-tab-change-scroll · case-summary-claim-amount)
- 누적 8248 테스트 통과
- Chip 잔여 0건 (모든 발견 결함 직접 fix 완료)
다음 세션 후보
Pack 4·5 wizard 도메인 분기 검증— 후속 #3 완료- 회수 단계 (집행문 발급 · 추심명령 · 배당) 까지의 확장
사무실 기억 (legacy) 5컬럼 kanban + OCR 승인 흐름— 후속 #3 Storage 2 P0 fix, dev restart 후 OCR 완전 흐름 검증 잔여판례·법령 RAG 탭 (Platform RAG 검색)— 후속 #3 완료- ops 콘솔 walkthrough (apps/ops 별도 OAuth 필요)
2026-05-15 후속 #3 — Pack 4·5 wizard + 사무실 기억 Storage P0 + Library RAG
후속 #2 의 다음 세션 후보 4건 중 3건 진행.
머지 PR (1건)
| PR | 분류 | 영향 | 회귀 가드 |
|---|---|---|---|
| #2315 | P0 두 단계 차단 | (1) tenants/{tid}/legacy-scanned/ · tenants/{tid}/feedback/ storage.rules 매칭 누락 → default deny. (2) apps/web/lib/firebase/admin.ts initializeApp({projectId}) 만 호출 → adminStorage.bucket() "Bucket name not specified" throw | storage-rules-coverage-invariants.test.ts (storage*Path 함수 전수 grep + Admin SDK 전용 화이트리스트) · admin-storage-bucket-invariants.test.ts (emulator+production storageBucket option + 3 단 fallback chain) |
시각 검증 — Pack 4·5 wizard 도메인 분기 완성
| Pack | 사건 유형 | Step 2 라벨 | Step 3 핵심 필드 |
|---|---|---|---|
| 1 | 대여금 | 채권자 / 채무자 | 대여 원금 · 이자율 · 대여일 · 변제기일 |
| 2 | 이혼 | 의뢰인 / 배우자 | 혼인 시작 · 혼인 종료 · 미성년 자녀 |
| 3 | 부동산 명도 | 임대인 / 임차인 | 부동산 목적물 주소 |
| 4 | 유류분 | 의뢰인 / 공동상속인 | 피상속인 성명 · 사망일 |
| 5 | 부당이득 반환 | 의뢰인 / 상대방 | 계약일 · 계약 종류 |
5/5 Pack 도메인 라벨·필드 완전 분기 확인 — wizard 의 도메인-aware 패턴 안전.
시각 검증 — 사무실 기억 빈 상태 + 업로드 폼
- 헤더: "사무실 기억 — 이 사무실이 일해 온 방식의 결과물 · 업무 유산(legacy)"
- 빈 상태 카피: "이 사무소가 일해 온 흔적이 아직 없습니다 / 과거 사건 서류를 스캔 업로드하거나 종결된 사건을 처리하면 사무소 기억으로 누적되어 다음 사건의 AI 검색·서류 초안에 활용됩니다" — 한 줄 본질 정합
- 기록 추가 폼: 3 카테고리 (사건 학습 / 사무소 운영 / 자문 참고) + 의뢰인명 + 사건 유형 + 연도 + 메모 + 파일 드래그 (PDF · 이미지, 최대 5장, 50MB)
- 상태 5종 filter (대기 / 처리중 / 검수 대기 / 완료 / 오류) + 보기 모드 3 (목록 / 파이프라인 / 연대기)
시각 검증 — 판례·법령 RAG (library)
- 헤더: "판례·법령 검색 — Platform RAG 에 수집된 법제처·대법원 판례·법령 전수 검색. 사건 컨텍스트 무관 자유 리서치"
- 2 모드: 조건 검색 / AI 법률 리서치
- 자주 찾는 키워드 13종 (소멸시효 · 민법 §162 · 대여금 · 임대차보증금 · 내용증명 · 지연손해금 · 이혼 재산분할 · 명도 청구 · 임대차 해지 · 유류분 · 상속재산분할 · 부당이득 · 계약 손해배상)
- 검색 동작 ✓ (emulator publicDocuments 비어 있어 0건 결과 + 정합 빈 상태 카피)
- AI 법률 리서치: 어시스트 1회 차감 + 실패 시 자동 환불 + 빠른 제안 3종 + Cmd/Ctrl+Enter 전송
누적 (2026-05-15 본 세션 + 후속 #1 + 후속 #2 + 후속 #3)
- 머지 PR 10건 (#2306·#2307·#2308·#2309·#2310·#2311·#2312·#2313·#2314·#2315)
- 회귀 invariant 9건 (external-script-url · theme-toggle-hydration · dashboard-listener-rules-coverage · portal-session-readonly · case-tab-change-scroll · case-summary-claim-amount · storage-rules-coverage · admin-storage-bucket · case-detail-scroll)
- 8252 테스트 통과
- chip 잔여 0건 — 모든 발견 결함 직접 fix 완료
다음 세션 후보
- dev server restart 후 사무실 기억 박스 업로드 OCR 완전 흐름 (admin.ts storageBucket 적용 + Cloud Function 발화 + legacyDocuments 상태 전이 검증)
회수 단계 (집행문 발급 · 추심명령 · 배당) 확장— 후속 #4 시각 검증 완료- sentDocuments 흐름 (서류 발송 + 송달 추적)
- ops 콘솔 walkthrough (apps/ops 별도 OAuth)
- 의뢰인 포털 서류 공유 (포털에서 변호사 finalized 서류 열람 흐름)
2026-05-15 후속 #4 — CaseInfoSection 합계 fix + 집행문 자동 생성 검증
후속 #3 의 다음 세션 후보 5건 중 2건 + 동일 결함 패턴 추가 sweep.
머지 PR (1건)
| PR | 분류 | 영향 | 회귀 가드 |
|---|---|---|---|
| #2317 | P1 동일 패턴 sweep | CaseInfoSection.tsx:49 도 PR #2313 와 동일 total = totalOutstanding ?? ... 결함 — 사건 정보 카드 "합계 0원" 표기. 원래 청구액 (principal + interestAmount + delayDamage) 으로 변경 | case-summary-claim-amount-invariants.test.ts 확장 — CaseInfoSection 도 totalOutstanding 직접 할당 금지 검증 |
시각 검증 (구체 수치)
CaseInfoSection 합계 (Pack 1 사건, 전액 변제 후):
- 이전: 원금 50,000,000원 · 이자액 5,917,808원 · 지연손해금 2,219,178원 · 합계 0원
- 이후: 원금 50,000,000원 · 이자액 5,917,808원 · 지연손해금 2,219,178원 · 합계 58,136,986원
집행문 자동 생성:
- 기일 탭의 "집행 단계" 카드 → "집행문 신청" 클릭 → 즉시 docId 생성 +
/cases/{caseId}/documents/{docId}자동 navigate - 페이지 title: "집행문 부여 신청서 — 박상철 — 김민호"
- 자동 prefill: 채권자/채무자 (이름·주소), 집행권원 ("서울중앙지방법원 2026가단99999 판결 정본 (2026-07-30 확정)"), 신청취지, 신청이유, 첨부 (판결 정본·확정증명원·송달증명원), 일자, 신청인
- 282 글자 / 82 낱말 / 6 목차 자동 생성
발견 결함 (다음 세션 chip)
(1) 집행문 편집 페이지 RSC 직렬화 에러 — 저장 실패: Cannot access toString on the server. You cannot dot into a temporary client reference from a server component. Server Action 에러 응답이 Server Component 에서 dot-access 됨. 사용자 시각에서는 에러 빨간 배너 표시.
(2) CaseSummaryCards SSR/Hydration mismatch (PR #2313 후 발생) — dev server bundle stale 또는 진짜 SSR/client 코드 분기. SSR: bg-primary + "회수 100%" / client: bg-emerald-500 + "전액 회수". dev restart 후 자동 해소 가능성. 진짜 결함이면 mounted-aware 패턴 적용.
누적 (2026-05-15 본 세션 + 후속 #1~#4)
- 머지 PR 12건 (#2306·#2307·#2308·#2309·#2310·#2311·#2312·#2313·#2314·#2315·#2316·#2317)
- 회귀 invariant 9건 (확장 1건 포함) — case-summary-claim-amount 가 CaseInfoSection 도 cover
- 8253 테스트 통과
- chip 잔여 2건 (집행문 RSC 에러 · CaseSummaryCards hydration)
다음 세션 후보
- dev restart 후 사무실 기억 OCR + CaseSummaryCards hydration 시각 재확인
집행문 편집 페이지 "Cannot access toString on the server" Server Action 에러 직렬화 fix— 후속 #5 완료- sentDocuments 흐름 (서류 발송 + 송달 추적)
- ops 콘솔 walkthrough
- 의뢰인 포털 서류 공유 + finalized 문서 download (포털 측 가시면)
2026-05-16 후속 #5 — 편집기 RSC binding 에러 fix (두 결함 동시)
후속 #4 의 chip 1번 집행문 편집 페이지 빨간 배너 해소.
머지 PR (1건)
| PR | 분류 | 영향 | 회귀 가드 |
|---|---|---|---|
| #2319 | 두 결함 동시 | (1) Tiptap setContent 직후 onUpdate 자동 발화 → 초기 로드 즉시 자동 저장 발화. 동일 내용 재저장 (의미 0) + transient RSC binding race. (2) toErrorMessage 가 RSC framework 내부 에러 메시지 사용자 시각에 노출 | in-platform-editor-suppress-auto-save-invariants.test.ts · error.test.ts 확장 |
Fix
(1) suppressNextAutoSaveRef: setContent 호출 3곳 (초기 로드 · 롤백 · AI 다듬기) 모두 직전 flag set → onUpdate 안에서 skip. 사용자 편집은 그대로.
(2) sanitizeFrameworkError: Cannot access toString on the server · temporary client reference · pass the value through to the client 포함 시 fallback + — 잠시 후 다시 시도해주세요 로 변환.
누적 (2026-05-15~16 전체)
- 머지 PR 13건 (#2306~#2319)
- 회귀 invariant 10건 (1건 확장 포함)
- 8258 테스트 통과
- chip 잔여 1건 (CaseSummaryCards SSR/Hydration — dev bundle stale 의심, dev restart 후 자동 해소 검증 필요)
다음 세션 후보
- dev restart 후 사무실 기억 OCR + CaseSummaryCards hydration + 편집기 자동 저장 race 시각 재확인 (확정/잠재 결함 모두 dev cold restart 의존)
- sentDocuments 흐름 (서류 발송 + 송달 추적)
- ops 콘솔 walkthrough (apps/ops 별도 OAuth)
의뢰인 포털 finalized 서류 download 가시면— 후속 #6 완료사건 finalize 흐름 (서류 finalize → 학습 루프 closure 검증)— 후속 #6 완료
2026-05-16 후속 #6 — 학습 루프 closure 시각 검증 + 포털 서류 열람
후속 #5 의 다음 세션 후보 4·5번 동시 진행. 사용자 메모리 정합: "ship 완료 메모리가 검증 부재를 가린다" — 코드만 보지 말고 시각 흐름 검증.
시각 검증 — ADR 0018 학습 루프 closure (한 줄 본질 핵심)
변호사 측 흐름 (사건 등록 → docgen → finalize):
- 집행문 신청서 자동 생성 (caseId 기반 prefill)
- "검토 완료" 버튼 → native confirm dialog → 확정 ✓
- "사무소 학습 1건" 카운터 등장 = ADR 0018 학습 루프 닫힘 시각 신호 ✓
- "확정됨 · 편집 잠김" 상태 + "의뢰인 공유" / "확정 해제" 버튼 등장
- "버전 이력 1건 (확정 직전 0회 편집)" 자동 기록
의뢰인 공유:
- "의뢰인 공유" 클릭 → 즉시 "공유됨" 상태 전이 (modal 없이)
시각 검증 — 의뢰인 포털 finalized 서류 열람
서류 탭 (포털 측):
- "서류는 열람 전용입니다. 무단 배포 및 제3자 제공을 금합니다." (보안 카피)
- "집행문 부여 신청서 — 박상철 NEW · 플랫폼 에디터 · 열람" ← finalize + 의뢰인 공유한 거 ✓
- "지급명령 신청서 (김민호) · 열람 불가" ← finalize 안 함 → 정확한 권한 게이팅 ✓
서류 viewer (/portal/docs/{docId}):
- 페이지 title: "집행문 부여 신청서 — 박상철 — 의뢰인 포털"
- 헤더: "포털로 돌아가기" + "인쇄/PDF" 버튼
- 본문: 당사자·집행권원·신청취지·신청이유·첨부·일자·신청인·법원 전체
- 푸터: "읽기 전용 · 무단 배포 및 제3자 제공을 금합니다." 보안 경고
발견 결함 (다음 chip)
(1) native window.confirm() 사용 — 사건 상세 → 서류 편집 → "검토 완료" 클릭 시 native browser confirm dialog. 모바일 위치·a11y·다크모드 일관성 anti-pattern. shadcn AlertDialog 패턴 (이미 프로젝트에 있음) 으로 마이그레이션 + 전수 sweep 필요. 사용자 메모리 정합: "한 패턴 발견 시 전수조사 sweep + 회귀 가드 cycle".
(2) finalize 직후 자동 저장 race — finalize 직후 "저장 실패: 확정된 문서는 편집할 수 없습니다" 빨간 배너. PR #2319 suppressNextAutoSaveRef 가 setContent 직후만 cover, finalize 시점 미cover. finalize action 직전 suppress flag set 추가 또는 finalize 후 saveTimer cancel 필요.
누적 (2026-05-15~16 전체)
- 머지 PR 13건 (#2306~#2319 + 회고 #2320)
- 회귀 invariant 10건
- 8258 테스트 통과
- chip 잔여 3건 (CaseSummaryCards hydration · native confirm · finalize race)
다음 세션 후보
- dev restart 후 사무실 기억 OCR + CaseSummaryCards hydration + 편집기 자동 저장 race 시각 재확인
native confirm/alert/prompt → shadcn AlertDialog 전수 마이그레이션— 후속 #7 baseline invariant shipfinalize 직후 자동 저장 race fix— 후속 #7 ship- sentDocuments 흐름 (서류 발송 + 송달 추적)
- ops 콘솔 walkthrough
2026-05-16 후속 #7 — finalize race fix + native dialog baseline invariant
후속 #6 의 chip 2건 모두 처리.
머지 PR (2건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2322 | finalize race | handleFinalize 시작 즉시 clearTimeout(saveTimerRef) + suppressNextAutoSaveRef = true + finalize 성공 후 setSaveState({kind:"idle"}) reset. "확정됨" 카드와 "저장 실패" 빨간 배너 공존 제거 |
| #2323 | baseline invariant | apps/web 전수 grep — 11 위치 native window.confirm/alert/prompt baseline 화이트리스트. 신규 도입 차단 + 마이그레이션 후 baseline 제거로 raise-the-bar |
11 위치 baseline
InPlatformDocEditor(3) ·EditableDocumentList·DocumentVersionListPortalLinkDialog·DebtSettlementTab·CaseNextActionsCardContractualDeadlineSection·InheritanceInfoSectionSnippetsSection·TiptapMarkdownEditor
회귀 가드 (2건)
in-platform-editor-suppress-auto-save-invariants.test.ts확장 (3 → 4 tests):- suppress set ≥ setContent + 1 (finalize)
- handleFinalize 본문에 clearTimeout · suppress set · saveState idle reset 존재
no-native-dialogs-invariants.test.ts신설 (2 tests):- baseline 외 신규 도입 차단 (apps/web 전수 grep)
- baseline orphan 차단 (raise-the-bar 강제)
누적 (2026-05-15~16 전체)
- 머지 PR 17건 (#2306~#2323)
- 회귀 invariant 12건 (1 확장)
- 8261 테스트 통과
- chip 잔여 1건 (CaseSummaryCards SSR/Hydration — dev cold restart 검증 필요)
다음 세션 후보
- dev cold restart 후 사무실 기억 OCR + hydration + finalize race + 편집기 race 시각 재확인 (확정/잠재 결함 4종)
- native dialog 점진 마이그레이션 (11 위치 → 0 위치, raise-the-bar)
- sentDocuments 흐름 (서류 발송 + 송달 추적)
- ops 콘솔 walkthrough (apps/ops OAuth)
사건 finalize → 새 사건 등록 시 사무실 기억 자동 참조 확인 (ADR 0041 두 RAG 결합 entry)— 후속 #8 완료
2026-05-16 후속 #8 — ADR 0041 두 RAG 결합 시각 검증 + native dialog 1 위치 마이그레이션
후속 #7 의 chip 잔여 1건 외 다음 세션 후보 중 진행 가능 2건 처리.
한 줄 본질 시각 검증 ✓ — ADR 0041 두 RAG 결합 entry
후속 #6 에서 finalize 한 집행문이 다음 같은 docKind docgen 시 사무실 기억 후보로 자동 참조되는지 시각 검증:
/docs/generate?caseId={caseId}&docType=집행문 진입:
AI 가 참고할 사무실 기억 (선택 1/1) 집행문 부여 신청서 — 박상철 .authored 2026차전123456 보기
핵심 시각 신호:
- "선택 1/1" — 방금 finalize 한 집행문 1건이 자동 후보로 추출 ✓
- 메타: 제목 · authored · 사건번호 — 변호사 검증 가능
- "보기" 버튼 — 원본 확인
이전 walkthrough (첫 docgen) 와 비교:
- 이전: "이 사건과 비슷한 사무실 기억을 찾지 못했습니다"
- 이후 (finalize 후): "선택 1/1 + 집행문 본"
한 줄 본질 완전 작동 시각 증거 ✓ — 사무소가 finalize 한 서류 → 다음 비슷한 사건 작성 시 자동 참조 후보.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2325 | raise-the-bar 시범 | ContractualDeadlineSection 삭제 confirm → shadcn AlertDialog (제목 + 설명 + 취소/삭제 footer). baseline 11 → 10 위치. invariant orphan 검증이 자동 재도입 차단 |
점진 raise-the-bar 패턴 정립
PR #2323 baseline 신설 → PR #2325 첫 마이그레이션. 이후 동일 패턴으로:
- AlertDialog 마이그레이션 commit
- baseline 화이트리스트에서 해당 파일 제거 commit (orphan 검증 통과)
- raise-the-bar 자동 진행
다음 마이그레이션 후보 (10 위치): SnippetsSection / EditableDocumentList / DocumentVersionList / PortalLinkDialog / DebtSettlementTab / CaseNextActionsCard / InheritanceInfoSection / TiptapMarkdownEditor / InPlatformDocEditor (3).
누적 (2026-05-15~16 전체)
- 머지 PR 19건 (#2306~#2325)
- 회귀 invariant 12건
- 8261 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart 검증 필요)
- 한 줄 본질 axis — finalize → 학습 누적 → 다음 docgen 자동 참조 시각 확인 ✓
다음 세션 후보
- dev cold restart 후 확정/잠재 결함 4종 시각 재확인
native dialog 마이그레이션 10 위치 → 0— 후속 #9 batch 2 ship (10 → 7)- sentDocuments 흐름 (서류 발송 + 송달)
- ops 콘솔 walkthrough
- 사건 신규 등록 + AI 빠른 등록으로 finalize 본 prefill 효과 검증
2026-05-16 후속 #9 — native dialog 마이그레이션 batch 2 (10 → 7)
후속 #8 의 #2325 시범 후 점진 raise-the-bar 가속.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2327 | batch 2 마이그레이션 | 3 위치 (SnippetsSection · EditableDocumentList · DocumentVersionList) confirm → shadcn AlertDialog + alert → toast.error. baseline 10 → 7 |
3 위치 마이그레이션 요지
- SnippetsSection:
'${label}' 을 삭제할까요?→ AlertDialog (dynamic title) - EditableDocumentList: 문서 soft-delete confirm → AlertDialog (Link 안에 nested 라 onClick preventDefault/stopPropagation 유지) +
alert()→toast.error - DocumentVersionList: 버전 롤백 confirm → AlertDialog +
alert()→toast.error
남은 baseline 7 위치
DebtSettlementTab(window.prompt 1 + confirm 1)PortalLinkDialog·InPlatformDocEditor(3 호출)InheritanceInfoSection(window.prompt)CaseNextActionsCardTiptapMarkdownEditor(prompt 1)
누적 (2026-05-15~16 전체)
- 머지 PR 20건 (#2306~#2327)
- 회귀 invariant 12건
- 8261 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart 검증)
- native dialog baseline: 11 → 7 (35% 마이그레이션 진행)
다음 세션 후보
- dev cold restart 후 확정/잠재 결함 4종 시각 재확인
native dialog batch 3— 후속 #10 ship (7→5)- sentDocuments 흐름
- ops 콘솔 walkthrough
- AI 빠른 등록 prefill 사무실 기억 효과 검증
2026-05-16 후속 #10 — native dialog batch 3 (7→5)
후속 #9 의 batch 2 후속.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2329 | batch 3 마이그레이션 | 4 confirm (CaseNextActionsCard promoteStatus · PortalLinkDialog 재발급 · InPlatformDocEditor.handleFinalize · handleUnfinalize) → shadcn AlertDialog. baseline 7 → 5 |
4 confirm 마이그레이션 요지
- CaseNextActionsCard — promoteStatus 액션만 AlertDialog 로 wrap (tab 액션은 그대로 onClick). 액션 별 분기 패턴.
- PortalLinkDialog 재발급 — "새 접속 코드 발급" 제목 + 의뢰인 알림 안내
- InPlatformDocEditor.handleFinalize — "이 문서를 확정합니다" + 학습 자료 안내
- InPlatformDocEditor.handleUnfinalize — sharedWithClient 분기 description ("의뢰인 공유 중" 경고)
남은 baseline 5 (모두 prompt — Dialog + Input + Form 필요)
DebtSettlementTab(prompt + confirm)InPlatformDocEditor(prompt 1 — 버전 저장 메모)InheritanceInfoSection(prompt)TiptapMarkdownEditor(prompt — 링크 URL)
누적 (2026-05-15~16 전체)
- 머지 PR 21건 (#2306~#2329)
- 회귀 invariant 12건
- 8261 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart 검증)
- native dialog 마이그레이션 진행: 11 → 5 (55%)
다음 세션 후보
- dev cold restart 후 확정/잠재 결함 4종 시각 재확인
native dialog batch 4— 후속 #11 ship (raise-the-bar 완주 5→0)- sentDocuments 흐름
- ops 콘솔 walkthrough
- AI 빠른 등록 prefill 사무실 기억 효과 검증
2026-05-16 후속 #11 — native dialog 마이그레이션 완주 (5→0)
batch 1 (#2325) → batch 2 (#2327) → batch 3 (#2329) → batch 4 (#2331) 최종.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2331 | raise-the-bar 완주 | 4 prompt → InputDialog + 1 confirm → AlertDialog. baseline 5 → 0 ✓. components/ui/InputDialog.tsx 신설 (controlled Dialog + Input + Form, validation, defaultValue) |
신설 — InputDialog 공용 wrapper
- Dialog + Input + Form
- controlled
open/onOpenChange defaultValue·placeholder·isValid·inputType(text/url/email)- cancel/ESC/외부 클릭 시
onConfirm호출 안 함 — native prompt 의 null 분기 정합
4 prompt + 1 confirm 마이그레이션
| 위치 | 변경 |
|---|---|
| TiptapMarkdownEditor (링크 URL) | InputDialog + isValid (http(s) 만) + alert→toast |
| InheritanceInfoSection (공동상속인 콤마) | InputDialog + isValid (non-empty) |
| InPlatformDocEditor (버전 메모) | InputDialog (empty 허용) |
| DebtSettlementTab (스냅샷 라벨) | InputDialog + default ${asOfDate} 계산본 |
| DebtSettlementTab (스냅샷 삭제 confirm) | controlled AlertDialog + pendingDelete state |
Native dialog baseline 완주
11 → 10 (#2325) → 7 (#2327) → 5 (#2329) → 0 (#2331) ✓
raise-the-bar 패턴 검증:
- baseline invariant 신설 (#2323)
- 점진 마이그레이션 batch 4회 (#2325·#2327·#2329·#2331)
- orphan 검증이 자동 재도입 차단
- 11 위치 → 0 위치 완주
누적 (2026-05-15~16 전체)
- 머지 PR 22건 (#2306~#2331 일부 제외)
- 회귀 invariant 12건 (1 확장)
- 8261 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart 검증)
- native dialog 완주 11→0 ✓
- 한 줄 본질 axis 시각 확인 완료 ✓
다음 세션 후보
- dev cold restart 후 확정/잠재 결함 시각 재확인 (CaseSummaryCards hydration)
sentDocuments 흐름— 후속 #12 코드 audit 완료 (UI 차단으로 우회)- ops 콘솔 walkthrough (apps/ops OAuth)
- AI 빠른 등록 prefill 사무실 기억 효과 검증 (새 사건)
다른 정책·규칙 invariant 패턴 sweep— 후속 #12 4 sweep + 1 baseline invariant ship
2026-05-16 후속 #12 — sentDocuments audit + 4 패턴 sweep + dangerouslySetInnerHTML baseline
emulator user reset 으로 UI walkthrough 차단 → 코드 audit + 추가 정책 invariant sweep 전환.
sentDocuments 흐름 audit (UI 우회)
발송 진입점·서버 액션·rules·UX 카피 모두 정합 ✓:
| 단계 | 위치 | 상태 |
|---|---|---|
| 진입점 | SendModal.tsx ("발송" 버튼 → recipient + method) | ✓ |
| Server Action | recordSentDocAction (doc-crud-actions.ts:141) | ✓ |
| Business Logic | @neohollo/business-logic/documents.recordSentDoc (ADR 0028) | ✓ |
| Side-effects | sentDocuments append + 원본 doc status batch + notification + activityLog | ✓ |
| List view | useDocs onSnapshot tenants/{tid}/sentDocuments + /docs 페이지 | ✓ |
| Firestore rules | sentDocuments/{id} read=staffOrOwner · write=false (Admin SDK 전용) | ✓ |
| UX 카피 | "실제 발송은 전자소송 시스템·이메일 등을 통해 직접 진행" (정직한 정책 명시) | ✓ |
발견 결함 0건.
4 패턴 sweep 결과
| 패턴 | 결과 | 평가 |
|---|---|---|
console.log 잔존 | 3건 (모두 logger utility · JSDoc 예시 — 정당) | 결함 0 |
// TODO/FIXME 마커 | 3건 (eval dataset 시니어 변호사 섭외 + portal-statute 이동 placeholder) | 결함 0 |
react-hooks/exhaustive-deps disable | 0건 | 결함 0 |
dangerouslySetInnerHTML | 3건 (모두 명시적 sanitization) | baseline 신설 |
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2333 | XSS baseline invariant | dangerouslySetInnerHTML baseline 3 위치 (layout · ContractRenewalReviewModal · portal docs viewer). 신규 도입 차단 + orphan 검증 — raise-the-bar 패턴 |
Raise-the-bar 패턴 재사용
native dialog 11→0 완주 (#2323~#2331) 후 동일 패턴 적용:
- baseline invariant 신설 (3 use sites whitelist)
- 신규 도입 시 invariant fail (apps/web 전수 grep)
- 마이그레이션 후 baseline 제거 → orphan 검증이 자동 차단
누적 (2026-05-15~16 전체)
- 머지 PR 23건 (#2306~#2333)
- 회귀 invariant 13건 (1 확장)
- 8263 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart 검증)
- native dialog 11→0 완주 ✓
- dangerouslySetInnerHTML baseline 3 (신규 도입 차단)
- 한 줄 본질 axis 시각 확인 완료 ✓
다음 세션 후보
- dev cold restart 후 확정/잠재 결함 시각 재확인 (마지막 chip)
- ops 콘솔 walkthrough (apps/ops OAuth)
- AI 빠른 등록 prefill 사무실 기억 효과 검증 (새 사건)
다음 invariant 패턴 후보— 후속 #13 sweep 7 패턴 완료, KST 4건 fix- ContractRenewalReviewModal markdown preview sanitization 강화 (DOMPurify 통합)
2026-05-16 후속 #13 — 7 패턴 sweep + KST 정책 4건 fix + invariant
후속 #12 의 sweep 패턴 확장. 7 anti-pattern sweep + KST 정책 위반 직접 fix.
7 패턴 sweep 결과
| # | 패턴 | 결과 | 평가 |
|---|---|---|---|
| 1 | .collection("...") 직접 호출 | 87건 | admin SDK 정상 패턴 — baseline 부적합 |
| 2 | as any | 0건 ✓ | CLAUDE.md "any 금지" 완전 정합 |
| 3 | await new Promise(setTimeout) | 3건 | 모두 retry backoff — 정당 |
| 4 | @ts-ignore / @ts-expect-error | 2건 | 모두 Firebase App Check 매직 변수 — 정당 |
| 5 | console.log | 3건 | logger utility · JSDoc — 정당 |
| 6 | TODO/FIXME | 3건 | placeholder — 정당 |
| 7 | react-hooks/exhaustive-deps disable | 0건 ✓ | 정당 |
| 8 | KST UTC 의존 | 4건 ❌ | 결함 — 직접 fix |
KST 정책 위반 4건 fix (#2335)
| 위치 | 결함 | Fix |
|---|---|---|
AuditLogSection.tsx:349 | CSV 파일명 new Date().toISOString().slice(0,10) — KST 오전 9시 이전 어제 | todayKST() |
DashboardCitationPreservationCard.tsx:117 | dateKey fallback 동일 UTC slicing | todayKST() |
finalize-renewal-draft-action.ts:149 | new Date().getFullYear() — server TZ 의존 | yearMonthKST().year |
finalize-renewal-draft-action.ts:154 | 동일 | yearMonthKST().year |
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2335 | KST 정책 sweep + fix | 4 UTC anti-pattern fix + kst-policy-invariants.test.ts 신설 (UTC slicing/server TZ getter 차단). 기존 audit-log-csv invariant todayKST() 사용 검증으로 갱신 |
코드 베이스 평가
7 sweep 패턴 중 결함 0건 7건, 결함 1건 (KST). production 코드 매우 깨끗.
CLAUDE.md 정책 (any 금지 · KST · TODO 최소) 정합 8/8 — 자율 fix·sweep cycle 누적 결과.
누적 (2026-05-15~16 전체)
- 머지 PR 24건 (#2306~#2335)
- 회귀 invariant 14건
- 8265 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart 검증)
- native dialog 11→0 완주 ✓
- dangerouslySetInnerHTML baseline 3 (신규 도입 차단)
- KST 정책 위반 0건 ✓
- 한 줄 본질 axis 시각 확인 완료 ✓
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인 (마지막 chip 종료)
- ops 콘솔 walkthrough (apps/ops OAuth)
- AI 빠른 등록 prefill 사무실 기억 효과 검증 (새 사건)
- ContractRenewalReviewModal markdown DOMPurify 통합
또 다른 정책 sweep — revalidatePath 누락 · Math.random()— 후속 #14 revenue revalidatePath fix + Math.random sweep 완료
2026-05-17 후속 #14 — revalidatePath 누락 sweep + Math.random sweep
후속 #13 의 7 sweep 후 추가 패턴 점검.
Sweep 1 — Server Action mutation 후 revalidatePath 누락
30+ 후보 파일 중 데이터 흐름 확인:
- 대부분 telemetry/event 기록 (writeActivityLog, audit log) — client onSnapshot 의존 → 정당
legacy-upload-actions.ts— legacy 페이지 onSnapshot 구독 → 정당revenue/actions.ts— SSR fetch + client onSnapshot 없음 → 결함 ❌
revenue/page.tsx 가 adminDb.collection("fees").get() 으로 SSR fetch + RevenueClient.tsx 가 onSnapshot 구독 없음. createInvoiceAction / markPaidAction 후 revalidatePath 누락 — 모달 close 후 재진입해도 새 데이터 안 보임.
Sweep 2 — Math.random() 비결정 사용
5 위치 모두 client ID 생성 / UI animation — 정당:
LeaseDepositForm.tsx(form local ID)DistributionModal.tsx(배당 row uid)useDocGeneration.ts(progress bar animation)legacy-upload-actions.ts(batch ID — Date.now() 결합)ScannedUploadModal.tsx(client batch ID)
baseline 부적합 — 결함 0건.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2337 | revenue SSR cache fix | createInvoiceAction · markPaidAction success path 에 revalidatePath("/revenue") 추가 + next/cache import. revenue-revalidate-path-invariants.test.ts 신설 (2 호출 보장 + 신규 mutation 추가 시 갱신 알림 분기) |
회귀 가드 (3건)
revenue-revalidate-path-invariants.test.ts:
revalidatePath('/revenue')호출 ≥ 2회next/cacheimport 존재export async function가 createInvoiceAction · markPaidAction 둘뿐 — 신규 mutation action 추가 시 invariant 갱신 알림
누적 (2026-05-15~17 전체)
- 머지 PR 33건 (#2306~#2337)
- 회귀 invariant 15건
- 8268 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart)
- native dialog 11→0 ✓
- dangerouslySetInnerHTML baseline 3
- KST 정책 위반 0건 ✓
- revenue SSR cache stale 0건 ✓
- 한 줄 본질 axis 시각 확인 완료 ✓
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인 (마지막 chip)
- ops 콘솔 walkthrough
- AI 빠른 등록 prefill 사무실 기억 효과 검증 (새 사건)
- ContractRenewalReviewModal markdown DOMPurify 통합
다른 SSR list view audit— 후속 #15 완료 (5 list view + ops)
2026-05-17 후속 #15 — 6 list view + ops revalidatePath audit + 3 추가 sweep
후속 #14 의 revenue fix 후 동일 패턴이 다른 list view 에도 있는지 전수 audit.
SSR list view audit (6 영역)
| 영역 | SSR fetch | client 구독 | revalidatePath 필요? |
|---|---|---|---|
/cases | adminDb.collection(...) | useCasesData onSnapshot ✓ | 불필요 — 정당 |
/clients | adminDb fetch | useClients onSnapshot ✓ | 불필요 — 정당 |
/calendar | adminDb fetch | useCalendar onSnapshot ✓ | 불필요 — 정당 |
/docs | adminDb fetch | useDocs onSnapshot ✓ | 불필요 — 정당 |
/legacy | adminDb fetch | useLegacyDocs onSnapshot ✓ | 불필요 — 정당 |
/activity | SSR fetch + loadMore action | onSnapshot 없음 (read-only 페이징) | 의도된 SSR 페이징 — 정당 |
/revenue | SSR fetch + onSnapshot 없음 | RevenueClient 구독 X | 결함 (후속 #14 fix #2337) |
revenue 만 유일한 결함 — 다른 6 영역 모두 onSnapshot 일관 패턴.
apps/ops audit
feature-flags—useDocumentSnapshot구독 ✓set-yjs-threshold·toggle-feature-flag— 같은 page 의 onSnapshot 의존 ✓seed-*-mock-action(5 위치) — test mock seeder, 결과 토스트 + 수동 reload ✓record-hypothesis-feedback·generate-anomaly-hypothesis— dashboard onSnapshot ✓
ops 결함 0건.
3 추가 sweep
| 패턴 | 결과 | 평가 |
|---|---|---|
.catch((err) => console.error(...)) silent swallow | 60건 | 모두 fire-and-forget 부수 효과 (writeActivityLog·writeNotification·markCaseMessagesRead) — 정당 |
as unknown as ... double cast | 10건 | 모두 type bridging (Firestore doc → typed · 외부 SDK 글로벌 · domain input → generic) — 정당 |
Math.random() 잔존 | 5건 | client ID 생성 / UI animation (이미 후속 #14) — 정당 |
종합 평가
- SSR + onSnapshot 일관 패턴 정합 — revenue 만 유일한 예외였음 (이미 fix)
- fire-and-forget 부수 효과 silent swallow — 의식적 패턴 (mutation 자체는 성공·부수 효과만 log)
- type bridging — 명시적 typing 으로 안전
- client ID 생성 — server 영향 없음
이번 세션 신규 fix 0건, audit 결과는 production 매우 깨끗 확인.
누적 (2026-05-15~17 전체)
- 머지 PR 33건 (#2306~#2337)
- 회귀 invariant 15건
- 8268 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart)
- 모든 정책 패턴 sweep 결과:
- native dialog 11→0 ✓
- dangerouslySetInnerHTML baseline 3 (XSS 차단)
- KST 정책 위반 0
as any0 ·console.log0 ·exhaustive-deps disable0- revenue SSR cache stale 0
- 6 list view + ops revalidatePath 일관
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인 (마지막 chip 종료)
- ops 콘솔 walkthrough (apps/ops OAuth)
- AI 빠른 등록 prefill 사무실 기억 효과 검증 (새 사건)
- ContractRenewalReviewModal markdown DOMPurify 통합
apps/opsshadcn 정합 sweep — apps/web 정합 (native dialog/KST baseline) 후 ops 측 추가 패턴
2026-05-17 후속 #16 — Server Action 200줄 baseline invariant
CLAUDE.md "새 Server Action 은 200줄 이하. 초과 시 _actions/ 분리" 정책 정합 invariant 신설.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2340 | raise-the-bar baseline | Server Action 200줄 초과 18 파일 baseline (488 → 208). 신규 도입 차단 + orphan 차단 invariant. countLines() 헬퍼 (wc -l 등가) |
Baseline 18 파일 (대표)
legacy-search-actions.ts 488 ← 최대
editable-document-actions.ts 383
doc-crud-actions.ts 271
portal-access-code-actions.ts 261
legacy-storage-actions.ts 257
quick-search-cases-action.ts 256
ai-assist-actions.ts 240
... 11 more files ...
finalize-document-action.ts 208 ← 최소
Raise-the-bar 패턴 4번째 적용 (누적)
- native dialog baseline (#2323) → 11→0 완주 (#2331)
- dangerouslySetInnerHTML baseline (#2333) — 3 위치 XSS 차단
- KST 정책 invariant (#2335) — 4 위치 fix + 신규 차단
- Server Action 200줄 baseline (#2340) — 18 위치 신규 차단
누적 (2026-05-15~17 전체)
- 머지 PR 35건 (#2306~#2340)
- 회귀 invariant 16건 (raise-the-bar baseline 4종)
- 8270 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart)
- 모든 정책 패턴 정합 ✓:
- native dialog 11→0
- dangerouslySetInnerHTML baseline 3
- KST 정책 위반 0
as any0 ·console.log0 ·exhaustive-deps disable0- revenue SSR cache stale 0
- Server Action 200줄 baseline 18 (raise-the-bar 진행 중)
- 한 줄 본질 axis 시각 확인 완료 ✓
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인
- ops 콘솔 walkthrough (OAuth)
- AI 빠른 등록 prefill 사무실 기억 효과 검증
- Server Action 200줄 baseline 마이그레이션 — 가장 큰 488줄 (legacy-search-actions) 분할 시도
- ContractRenewalReviewModal markdown DOMPurify 통합
2026-05-17 후속 #17 — 첫 한 줄 본질 핵심 기능 ship (사무소 기억 자가 회귀)
정직한 평가 (사용자 질문에 응답)
후속 #14~#16 모두 정합 가드 · UI 결함 fix · invariant baseline 신설. 한 줄 본질 직접 기여 신규 capability 0건이었음.
사용자 메모리 정합 — "회의·자율 세션은 핵심 기능을 만들지 못함" / "혁신 core 우선" / "학습 루프는 데이터로 닫힌다 — 합성 corpus + 자가 회귀로 내부 closed loop".
사용자 명시 scope (사무소 기억 학습 품질 closed-loop) 로 전환.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2342 | 첫 한 줄 본질 핵심 기능 | ADR 0015 Tenant RAG leave-one-out 자가 회귀 invariant. 합성 corpus 8건 (4 사건 × 2 docKind) + deterministic BoW embedding + recall@3 ≥ 0.5 임계값 |
Ship 한 capability
ADR 0015 Tenant RAG 학습 루프 닫힘 정량 검증:
- 합성 corpus 8건 (Case A·B·C 대여금 + Case D 공사대금, 각각 지급명령 + 답변서)
- 같은 case 본은 당사자·금액·법원 공유 (높은 의미 유사도)
- 다른 case 의 같은 docKind 는 패턴 (조항·구문) 공유 (중간 유사도)
- leave-one-out: 각 본 query → 나머지 7건 인덱스 → top-3 cosine retrieval
- ground truth: 같은 caseId OR 같은 docKind = positive
- recall@3 ≥ 0.5 통과 ✓
회귀 가드 — corpus 또는 embedding 변경 시 recall@3 자동 측정 + 임계값 미달 시 fail.
Production 단계 (후속)
mockEmbed(BoW 한국어 unigram+bigram) →text-embedding-004호출로 교체- 동일 corpus·동일 ground truth 로 production embedding recall@3 측정 (≥0.7 기대)
- 임계값 raise — 회귀 가드 강화
누적 (2026-05-15~17 전체)
- 머지 PR 38건 (#2306~#2342)
- 회귀 invariant 17건 (raise-the-bar baseline 4종 + 한 줄 본질 자가 회귀 1종)
- 8275 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart)
- 정합 baseline + 한 줄 본질 ship 균형 회복 ✓
다음 세션 후보 (한 줄 본질 직접 기여 우선)
mockEmbed → text-embedding-004— 후속 #18 ship (scripts/eval-tenant-rag-self.ts)두 RAG 결합 retrieval ops dashboard— 이미 ADR 0043 v2.68·v2.73 ship 확인AI 서류 품질 코파일럿 v2 anomaly 자동 dismiss— 후속 #18 ship (Phase 3 pure functions)사건 자동 분류 정밀도— 후속 #18 ship (corpus 42건 + invariant)- (정합 가드 후순위) Server Action 200줄 마이그레이션 / DOMPurify 통합
2026-05-17 후속 #18 — 한 줄 본질 핵심 기능 4건 일괄 ship
사용자 요청 "핵심 기능 후보 5건 모두 진행". 후속 #17 (Tenant RAG self-regression) 이후 추가 4건 진행.
머지 PR (2건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2344 | 한 줄 본질 #2·#3 | (1) 사건 자동 분류 정확도 corpus 42건 + classifyByKeywords invariant (≥90% 통과) + (2) ADR 0042 Phase 3 — shouldAutoDismiss + shouldPromoteToOpsKnowledge pure functions + 18 unit tests |
| #2345 | 한 줄 본질 #4 | scripts/eval-tenant-rag-self.ts — PR #2342 mock invariant 의 production embedding 버전 (Vertex AI text-embedding-004). 동일 corpus·ground truth + --apply 시 ragMergeQuality 저장 → 기존 RagHistoryChart 자동 표시 |
후보 5건 결과
- 사건 자동 분류 정밀도 ✓ — corpus 42건 (14 종 × 3), classifier ≥ 90% 정확도 통과
- anomaly Phase 3 자동 dismiss/promote ✓ — pure functions 4종 + 18 unit tests (운영자 시나리오 포함)
- ragMergeQuality ops dashboard 시각화 ✓ — 이미 ADR 0043 v2.68/v2.73 에 ship 됨 확인 (별도 작업 0)
- mockEmbed → text-embedding-004 교체 ✓ — scripts/eval-tenant-rag-self.ts 신설 (production embedding hook)
- ContractRenewalReviewModal DOMPurify / Server Action 200줄 마이그레이션 — 정합 후순위로 보류
한 줄 본질 핵심 기능 누적 (후속 #17 + #18)
| 기능 | PR | 상태 |
|---|---|---|
| Tenant RAG leave-one-out 자가 회귀 (mock) | #2342 | ✓ |
| 사건 자동 분류 정확도 (42건 corpus) | #2344 | ✓ |
| anomaly Phase 3 자동 dismiss/promote | #2344 | ✓ |
| Tenant RAG production embedding eval script | #2345 | ✓ |
| 두 RAG 결합 ragMergeQuality 시각화 | (기존) | ✓ ship 확인 |
5개 핵심 기능 모두 ship.
누적 (2026-05-15~17 전체)
- 머지 PR 42건 (#2306~#2345)
- 회귀 invariant 20건 (자가 회귀 1종 + 분류 1종 + anomaly 1종 + 기존)
- 8301 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration)
- 한 줄 본질 핵심 기능 5건 ship 완료 ✓
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인
— 후속 #19 확인:record-hypothesis-feedback-actionautomationscripts/promote-past-hypotheses.ts와evaluatePromotionEligibilitypure functions + 기존 invariant 로 이미 ship— 후속 #19 확인:casePrefillRuns누적 결과 ops dashboard/case-prefill페이지 +useCasePrefillRunshook 으로 이미 ship— 후속 #19 가이드 shipscripts/eval-tenant-rag-self.tscron 통합14 종 외 신영역— 후속 #19 Pack 6 형사 사건 ADR 0045 draft
2026-05-17 후속 #19 — eval scripts cron + Pack 6 신영역 ADR draft
사용자 메모리 정합 — "ship 완료 메모리가 검증 부재를 가린다". 후속 #18 의 다음 세션 후보 5건 중 #2·#3 은 이미 ship 됨 발견 (실 코드 확인). 새 capability 는 #4 cron 가이드 + Pack 6 ADR draft.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2347 | 한 줄 본질 #19 | (1) scripts/eval-case-classification-gemini.ts — production Gemini 분류 정확도 측정. (2) eval-scripts-cron-setup.md — 3 eval scripts cron 일정·아키텍처 가이드. (3) ADR 0045 draft — Pack 6 형사 사건 신영역 |
이미 ship 됨 확인 (재발견)
| 기능 | 위치 | 상태 |
|---|---|---|
| anomaly Phase 3 자동 dismiss/promote | scripts/promote-past-hypotheses.ts + evaluatePromotionEligibility pure functions + business-logic-ops-anomaly-promote.test.ts | ✓ batch script + invariant |
| casePrefillRuns ops dashboard | apps/ops/app/(ops)/case-prefill/page.tsx + useCasePrefillRuns | ✓ collectionGroup 구독 + 필드 별 통계 |
Pack 6 형사 사건 ADR 0045 (draft)
14 종 외 첫 신영역. 사용자 합의 대기:
| 영역 | 추정 작업량 |
|---|---|
| recoveryType + types | 200 lines |
| wizard 3 step 분기 | 600 lines |
| 사건 상세 패널 | 400 lines |
| docKind 6 추가 + 템플릿 | 500 lines + .docx |
| 시효·기한 | 200 lines |
| 9 단계 흐름 분기 | 300 lines |
| corpus 보강 | sync-law 작업 |
| 회귀 invariant + e2e | 400 lines |
| 합계 | ~2600 lines + .docx 6 |
Pack 6 first ship = 사기 1 종. 후속 4 종 (횡령·폭행·마약·교통치사상) 별도 PR.
누적 (2026-05-15~17 전체)
- 머지 PR 44건 (#2306~#2347)
- 회귀 invariant 20건
- 8301 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration)
- 한 줄 본질 핵심 기능 ship 누적:
- #17: Tenant RAG mock 자가 회귀
- #18: 사건 분류 + anomaly Phase 3 + production embedding eval (5/5)
- #19: Gemini classification eval + cron 가이드 + Pack 6 ADR draft
다음 세션 후보
- Pack 6 사용자 합의 후 implementation — 14 → 15 종 recoveryType + wizard 형사 분기 + docKind 6
- dev cold restart 후 hydration 결함 시각 재확인 (마지막 chip)
- production Gemini classification 측정 baseline 후 임계값 raise
- ragMergeQuality kind 별 chart 분리 노출 (현재 통합 시계열)
- 또 다른 한 줄 본질 영역 — exemplar selection 알고리즘 개선 / RAG cache hit rate
2026-05-17 후속 #20 — Pack 6 형사 사건 first slice ship (14 → 15 종)
사용자 요청 "핵심 기능 후보 모두 진행". 14 종 외 첫 신영역 ship — 한 줄 본질 도메인 직접 확장.
머지 PR (1건)
| PR | 분류 | 핵심 변경 |
|---|---|---|
| #2349 | 한 줄 본질 도메인 확장 | ADR 0045 Pack 6 형사 사건 first slice. 14 → 15 종 recoveryType (criminal-fraud). recovery-input-schemas + LABEL + description + icon + statute + wizard Step1 SelectItem + checklist 카탈로그 (client·firm) + 자동 분류 corpus 3건 + 7 회귀 가드 update + 4 type narrowing 분기 |
Ship 한 capability
- 15 종 recoveryType — Pack 1~5 14 종 + Pack 6 형사 사기 1 종
- wizard 진입 가능 — Step1 SelectGroup "형사 (Pack 6)" → 사기 옵션
- checklist — 의뢰인 자료 3종 (출석 통지서·진술서·방어 증거) + 사무소 서류 3종 (변호인 선임서·의견서·양형 자료)
- 자동 분류 —
classifyByKeywords형사 키워드 rule (변호인·송치·피고인·보이스피싱·양형·구속 영장·형법) 우선순위 110 - 공소시효 — 형사소송법 §249 placeholder (10년)
- 시각 신호 — Gavel 아이콘 + red-600 accent (사건 심각도)
- placeholder UI — Pack 4 (상속) 패턴 — 도메인 본격 UI 는 후속 PR Pack 6.2
후속 PR (Pack 6.2~)
| 후속 | 내용 |
|---|---|
| Pack 6.2 | DocKind enum 정식 등재 (criminal-* 3 종 → doc-kind-to-ko.ts SSoT) |
| Pack 6.3 | CriminalInfoSection 도메인 패널 (overview 탭) |
| Pack 6.4 | 9 단계 흐름 분기 (변호인선임→수사→기소→1심→항소→상고) |
| Pack 6.5 | 공소시효 일반 7년 · 특경 10년 union 확장 |
| Pack 6.6 | 후속 4 종 (횡령·폭행·마약·교통치사상) |
| Pack 6.7 | 형법 · 형사소송법 corpus sync (publicDocuments) |
한 줄 본질 정합
사무소가 처리한 형사 의견서·양형자료가 다음 사건 자동 참조.
ADR 0015 Tenant RAG + ADR 0041 두 RAG 결합 패턴 그대로 적용 — 도메인 분기만 새로.
누적 (2026-05-15~17 전체)
- 머지 PR 47건 (#2306~#2349)
- 회귀 invariant 20건
- 8302 테스트 통과
- chip 잔여 1건 (CaseSummaryCards hydration · dev restart)
- 한 줄 본질 핵심 기능 누적:
- #17: Tenant RAG mock 자가 회귀
- #18: 사건 분류 + anomaly Phase 3 + production embedding eval (5/5)
- #19: Gemini classification eval + cron 가이드
- #20: Pack 6 형사 사건 first slice (14 → 15 종) ← 도메인 직접 확장
다음 세션 후보
- Pack 6.2 — CriminalInfoSection 도메인 패널 + DocKind enum 정식 등재
- Pack 6.5 — 공소시효 정밀 분기 (일반 7년 · 특경 10년)
- dev cold restart 후 hydration 결함 시각 재확인
- production Gemini classification 측정 baseline
- ragMergeQuality kind 별 chart 분리 노출
2026-05-17 후속 #21 — Pack 6.2 ship + 학습 인프라 정교화 (4 capability)
후속 #20 의 "다음 세션 후보" 4건을 1 세션에 일괄 ship — 핵심 기능 직접 확장 + 학습 루프 정교화.
머지 PR (3건, branch feat/pack6-dockind-22-criminal)
| PR (branch) | 분류 | 핵심 변경 |
|---|---|---|
| 634e81d9 | DocKind enum 19 → 22 | criminal-defense-appointment · criminal-opinion · criminal-sentencing-materials. 14 SSoT 동시 update (docgen-types · docgen-telemetry · doc-kind-to-ko · v2-query-expansion · related-memory-query · docx-upload-validation · DocxTemplatesSection · ops doc-kinds · functions ops-generate-test-doc · 4 invariant tests + ops UI text 3 곳) |
| 22bb7236 | CriminalInfoSection 도메인 패널 | overview 탭 형사 사건 정보 카드 — 죄명·사건번호·진행 단계·검찰청·법원·송치일·기소일·구속·핵심 사실. 새 case-criminal-actions Server Action (ADR 0028 thin wrapper) + 9 CaseData 형사 필드 + 13 단위 테스트 |
| 147b245e | ragMergeQuality kind 별 chart 분리 | RagEvalKind 3 종 (eval-rag-merge / tenant-rag-self / case-classification-gemini). normalizeRagMergeRow + seriesByKind. dashboard + tenants/[tid] 모두 kind 별 별도 chart 렌더. kind 누락 docs 는 eval-rag-merge backward-compat. 9 normalize 단위 테스트 |
| 3e2e18d0 | exemplar selection v2 (Wilson + recency) | (up-down)/total → wilsonLowerBound(up, total) + 0.5) × recencyFactor. 표본 크기 자연 가중 (1/1 ≈ 0.21 vs 10/10 ≈ 0.72) + halfLife 180일 exponential decay. minSample binary cliff 폐기. selectionScore optional 출력. 4 새 v2 동작 테스트 |
Ship 한 capability
- 22 DocKind 정식 등재 — Pack 6.2 변호인 선임서·의견서·양형 자료 3 종.
docx-upload-validation+DocxTemplatesSection가DOC_KIND_LABELderive 로 자동 동기화 (개발자 수동 sync 부담 0) - 형사 사건 도메인 패널 — Pack 4 (상속)
_inheritance/패턴 모사. 송치일 → 기소일 순서 검증 (criminalIndictmentDate < criminalReferralDate거부) - kind 별 학습 회귀 chart — recall + precision (eval-rag-merge), recall (tenant-rag-self), accuracy (case-classification-gemini) 의 의미 혼합 회피
- exemplar selection v2 — Wilson 신뢰 하한으로 표본 크기 차등 + recency exponential decay 로 신선·고품질 우대. cold start 안전 (quality 없으면 순수 recency)
기술 결정
| 결정 | 이유 |
|---|---|
| Wilson 95% (z=1.96) | Reddit·Hacker News 가 동일 알고리즘 사용. 표본 < 5 에서도 부드러운 점수. |
| recency halfLife 기본 180일 | 6 개월 = 사무소 사건 주기 1 cycle. 너무 짧으면 학습 자산 손실, 너무 길면 옛 패턴 잔재. |
| qualityMinSample (v1 필드) deprecated noop | API 호환만 유지. 다음 PR 에서 호출부 정리 후 제거. v2 의 Wilson 이 표본 자연 가중하므로 minSample gate 불필요. |
eval-rag-merge writer 에 kind 추가 | forward symmetry. 기존 문서 (kind 없음) 는 normalizeRagMergeRow 에서 "eval-rag-merge" 로 자동 분류 — backward compat 손실 0. |
| RecoveryTab 형사 placeholder 유지 (제거 안 함) | recovery 탭 자체는 채권 회수 도구라 형사 무관. 안내 메시지로 overview 탭의 CriminalInfoSection 참조 가이드. |
검증
- apps/web type-check 통과
- apps/ops type-check 통과
- functions type-check 통과
- apps/web 703 test files (8319 tests) 통과 (+17 신규)
- apps/ops 107 test files (845 tests) 통과 (+11 신규)
- lint 0 errors
한 줄 본질 정합
이번 세션은 한 줄 본질 ("법률 사무소가 자기 데이터로 자기 AI 학습·활용") 의 두 축 모두 정교화:
- 도메인 직접 확장 축 — Pack 6 형사 도메인이 22 DocKind 까지 SSoT 등재 완료. CriminalInfoSection 으로 형사 사건의 사무소-특화 메타 (송치일·기소일·구속 등) 입력 통로 ship.
- 학습 알고리즘 정교화 축 — exemplar selection v2 가 표본 크기에 비례한 신뢰 점수 + 신선도 곱셈으로 사무소 변호사 피드백을 제대로 반영. ragMergeQuality kind 별 chart 분리로 어떤 학습 axis 가 향상되는지 명확.
누적 (2026-05-15~17 전체)
- 머지 PR 47 + 4 = 51건 (#2306~#2349 + branch 4 commits)
- 회귀 invariant 20+ ~ 누적
- 8319 web + 845 ops = 9164 테스트 통과
- 한 줄 본질 핵심 기능 누적:
- #17: Tenant RAG mock 자가 회귀
- #18: 사건 분류 + anomaly Phase 3 + production embedding eval (5/5)
- #19: Gemini classification eval + cron 가이드
- #20: Pack 6 형사 사건 first slice (14 → 15 종)
- #21: Pack 6.2 DocKind 22 등재 + CriminalInfoSection + RAG kind chart + exemplar v2 ← 도메인 패널 + 학습 정교화
다음 세션 후보
- Pack 6.5 — 공소시효 정밀 분기 (형법 §249 일반 7년 · 특정경제범죄법 §3 10년 union)
- Pack 6.4 — 9 단계 형사 사건 흐름 (변호인선임 → 수사 → 송치 → 기소 → 1심 → 항소 → 상고 → 확정 → 집행유예/실형 분기)
- Pack 6.6 — 후속 4 종 (횡령·폭행·마약·교통치사상)
- dev cold restart 후 hydration 결함 시각 재확인
- production Gemini classification 측정 baseline (real tenant 데이터)
- exemplar selection v2 → 실제 fetchAuthoredExemplars 호출부에서
nowMs명시 전달 +qualityMinSample호출부 정리
2026-05-17 후속 #22 — Pack 6 형사 도메인 완성 (4 capability)
후속 #21 의 "다음 세션 후보" 4건 (Pack 6.4 + 6.5 + 6.6 + exemplar 정리) 일괄 ship. Pack 6 형사 도메인이 시효·9 단계 흐름·5 종 죄명·timeline 시각화까지 모두 production.
머지 PR (4 commit, branch feat/pack6-criminal-statute-and-more)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| 8828053c | Pack 6.5 공소시효 정밀 분기 | yearsApplied union 에 7 추가 (1|2|3|5|7|10). criminalAggravated 토글 — 형법 §347 일반 7년 vs 특경법 §3 가중 10년. CaseData 2 신규 필드 (criminalAggravated · criminalCrimeEndDate). statute-adapter 기산점 분기 (3 단계 fallback). CriminalInfoSection UI 갱신 |
| c46e63b8 | Pack 6.6 형사 4 종 추가 | RecoveryType 15 → 19 (criminal-embezzlement · criminal-assault · criminal-drug · criminal-traffic-fatality). 각 죄명에 일반·가중 시효 분기. 11 SSoT 동시 update + 5 invariant 갱신. 12 합성 corpus + 4 키워드 룰 |
| ec34975e | Pack 6.4 9 단계 형사 흐름 + timeline | criminalPhase union 4 → 9 (변호인선임 → 수사 → 송치 → 기소 → 1심 → 항소 → 상고 → 확정 → 집행). 신규 SSoT _criminal/criminal-phase.ts (CRIMINAL_PHASES + LABEL + HINT + normalize backward-compat + progress + closed). CriminalInfoSection PhaseTimeline 컴포넌트 (9 segment progress bar). 16 신규 단위 테스트 |
| bb2d9234 | exemplar v2 cleanup | fetchAuthoredExemplarsAction 가 nowMs 명시 전달. qualityMinSample deprecated 필드 SelectExemplarsInput 에서 제거. synthetic-firm-memory-loop 2 호출부 + fixture 주석 + deprecated 호환 테스트 정리 |
Ship 한 capability
- 19 RecoveryType (총) — Pack 1 6 + Pack 2 1 + Pack 3 3 + Pack 4 2 + Pack 5 2 + Pack 6 5 (사기·횡령·폭행·마약·교통치사상)
- 9 단계 형사 흐름 timeline — 변호인이 한눈에 사건 단계 파악 + 진척률 11.1% → 100% 표시
- 공소시효 7·10년 분기 — 가중처벌 (특경법 §3 · 업무상 §356 · 영리 §58 · 특가법 §5의3·§5의11) 토글로 정밀 시효
- 52 corpus + 4 키워드 룰 — Pack 6 형사 자동 분류 (False positive 차단 · priority 120 죄명 + 110 fallback)
- exemplar selection v2 cleanup — qualityMinSample noop 필드 제거, nowMs 명시 전달
기술 결정
| 결정 | 이유 |
|---|---|
| criminalAggravated boolean (단일 toggle) | 5 종 각 죄명에 "가중처벌 여부" 분기를 일관 패턴으로 — 죄명별 sub-enum 보다 단순. UI 도 가/아니오 Select 단일 |
| criminalCrimeEndDate 신규 필드 | 형사소송법 §252 ② 공소시효 기산점. proxy fallback (송치 → 기소) 으로 미입력 케이스 graceful degradation |
| 9 단계 timeline 색 — red 계열 (3 tier: done red-500 / current red-600 / future muted) | 사건 심각도 시각 신호 유지 + 진척 위치 즉시 인지 |
| normalizeCriminalPhase 함수 | 기존 Pack 6.2 데이터 (4 단계) 가 prod 에 없지만 안전망. "prosecution" → "referred" · "trial-appeal" → "appeal" 보수적 매핑 |
| qualityMinSample 즉시 제거 | v2 Wilson 이 자연 표본 가중 — minSample binary cliff 무의미. 호출부에서 이미 noop, 인터페이스 정리로 의도 명확화 |
검증
- apps/web type-check 통과
- apps/ops type-check 통과
- apps/web 704 test files (8354 tests) 통과 (+25 신규)
- Pack 6.5 statute 5 + 4 validate
- Pack 6.6 statute 9
- Pack 6.4 phase 16 — normalize · progress · closed · label 완전성
- apps/ops 107 test files (845 tests) 통과
- 0 lint errors
한 줄 본질 정합
사무소가 처리한 형사 의견서·양형자료가 다음 사건 자동 참조 (5 죄명·9 단계 흐름·정밀 시효 분기).
본 세션의 모든 변경이 한 줄 본질의 도메인 확장 축에 직접 기여:
- 5 종 죄명 — 형사 사건 데이터 capture surface 가 1 → 5 종 (5배). 사무소 형사 케이스 분류·검색·exemplar 학습 모두 5 종 분리
- 공소시효 정밀 — 변호인 실무에서 면소·시효 다툼은 핵심. 가중처벌 토글 한 번으로 7/10년 분기 (이전엔 10년 fallback)
- 9 단계 timeline — 사무소가 N 형사 케이스를 portfolio 로 관리할 때 어느 단계에 몰려 있는지 가시화. 단계별 dashboard widget 후속 가능
- exemplar 정리 — production 안정성 +1, 다음 학습 루프 변경 시 깔끔한 출발점
누적 (2026-05-15~17 전체)
- 머지 PR 51 + 4 = 55건 (#2306~#2351 + 4 commits)
- 회귀 invariant 누적 갱신 — RECOVERY_TYPES 19 종 invariant 6 곳 (#22 에서 갱신)
- 8354 web + 845 ops = 9199 테스트 통과
- 한 줄 본질 핵심 기능 누적:
- #17: Tenant RAG mock 자가 회귀
- #18: 사건 분류 + anomaly Phase 3 + production embedding eval (5/5)
- #19: Gemini classification eval + cron 가이드
- #20: Pack 6 형사 사건 first slice (14 → 15 종)
- #21: Pack 6.2 DocKind 22 등재 + CriminalInfoSection + RAG kind chart + exemplar v2
- #22: Pack 6.4+6.5+6.6 — 9 단계 흐름 + 정밀 시효 + 5 종 죄명 + exemplar cleanup ← Pack 6 완성
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인 (retro #20·#21 cumulative)
- production Gemini classification 측정 baseline — real tenant 데이터 19 종 corpus accuracy
- CriminalInfoSection 의 9 단계 timeline 클릭 시 단계별 일자 자동 입력 UX
- 형사 docKind 3 종 (변호인선임서·의견서·양형자료) 실 PDF/DOCX 템플릿 작성 + 사무소 업로드 검증
- Pack 6 dashboard widget — 9 단계 portfolio 분포 (각 단계 case 수 + 시효 임박 알림)
- recovery-input-schemas RECOVERY_TYPES → CaseDomainType rename (ADR 0028 예약 작업)
2026-05-17 후속 #23 — Pack 6 portfolio 가시화 + 학습 인프라 정착 (4 capability)
후속 #22 의 "다음 세션 후보" 6건 중 자율 모드 적합한 4건 일괄 ship — Pack 6 형사 도메인이 dashboard portfolio + 자동 stamp + 템플릿 spec + 명명 cleanup 완성.
머지 PR (4 commit, branch feat/pack6-dashboard-and-cleanup)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| 6c79b86c | Pack 6.7 dashboard widget | DashboardCriminalPortfolioCard — 5초 헤드라인: 활성/종결 + 시효 임박/구속/가중 alert + 9 단계 흐름 분포 + 5 죄명 chip. computeCriminalPortfolio 순수 함수 + Server Action (1000 case scan). 16 신규 단위 테스트 |
| 14eef80d | Pack 6.7 phase 자동 stamp | CriminalInfoSection Select onChange 에서 phase 진입 시 referralDate/indictmentDate 비어 있으면 todayKST() 자동 stamp. autoStampForPhaseTransition pure 함수 — referred/indicted/trial-* 단계별 일자 자동 채움. 7 신규 단위 테스트 |
| ccd0c1f4 | Pack 6.8 docKind placeholder 어댑터 | buildCriminalDocxRenderData — 3 형사 docKind (변호인 선임서·의견서·양형자료) 의 placeholder map 빌더. 18 공통 + docKind 별 추가 2~3. apps/docs 에 criminal-templates.md 작성·업로드 가이드. 13 신규 단위 테스트 |
| 8a6d3daf | CaseDomainType alias (ADR 0028) | RECOVERY_TYPES → CaseDomainType 점진 rename. additive alias 4 (CaseDomainType · CASE_DOMAIN_TYPES · CASE_DOMAIN_TYPE_LABEL · CaseDomainTypeSchema). CLAUDE.md 새 섹션. 4 alias drift 회귀 가드 |
Ship 한 capability
- Pack 6 dashboard 5초 헤드라인 — 사무소 형사 사건 portfolio (활성/종결 · 시효/구속/가중 alert · 9 단계 흐름 분포 · 5 죄명 chip). 활성 0건이면 자체 hide
- phase 진입 = 일자 stamp 자동화 — 변호인 단계 클릭 한 번으로 송치일·기소일 timeline 일자 stamp. 기존 일자는 보존
- 형사 docKind 3 종 placeholder + 가이드 — buildCriminalDocxRenderData 어댑터 + criminal-templates.md operations 가이드. 사무소가 가이드 따라 .docx 작성·업로드 가능
- CaseDomainType alias — Pack 6.7 까지 19 종 누적 후 명명 일반화. 신규 코드 권장 path 명확, 기존 1785 참조 그대로
기술 결정
| 결정 | 이유 |
|---|---|
| 활성 0건 자체 hide | 비형사 사무소에서 dashboard 노이즈 회피. Pack 1~5 위주 사무소가 형사 카드 보이는 건 부정확 |
| computeCriminalPortfolio 가 computeCaseStatuteStatus 재사용 | 시효 임박 카운트 자동 (3 단계 fallback). 중복 로직 0 |
| autoStampForPhaseTransition 가 pure function | 단위 테스트 가능 + UI 만 thin wrapper. 자동 stamp 규칙 변경 시 테스트만 갱신 |
| 자동 stamp 는 비어 있을 때만 | 변호인이 정확한 일자 입력했다면 절대 덮어쓰지 않음 (data loss 회피) |
| docKind 어댑터 분리 (criminal-docgen-placeholders.ts) | 민사 docgen-adapter 는 RecoveryCalculateResult 의존, 형사는 청구액 없음. 두 어댑터 분리 — 양쪽 책임 명확 |
| CaseDomainType additive alias | 1785 ref 전수 sweep 위험 회피. 신규 코드 권장 path 명시 + drift 회귀 가드로 의미 동결 |
검증
- apps/web type-check 통과
- apps/web 706 test files (8394 tests) 통과 (+40 신규)
- Pack 6.7 portfolio 16 + auto-stamp 7
- Pack 6.8 docgen-placeholders 13
- CaseDomainType alias 4
- apps/ops 107 test files (845 tests) 통과
- 0 lint errors
한 줄 본질 정합
사무소 형사 portfolio (5 죄명 × 9 단계 × 시효) 가시화 + 자동 stamp + 템플릿 spec 으로 변호인 실무 마찰 감소.
본 세션의 모든 변경이 한 줄 본질에 기여:
- portfolio 가시화 — 사무소가 N 형사 사건을 한눈에 파악. 어느 단계, 어느 시효, 어느 죄명에 분포하는지 dashboard 가 자동 집계
- 자동 stamp — phase 변경 = 일자 입력. 변호인 데이터 capture 마찰 ↓ → 사무소 형사 데이터 quality ↑
- docKind 어댑터 — 사무소가 자기 .docx 템플릿 작성·업로드하면 모든 후속 형사 사건의 1-click 생성 활성화 (사무소 학습 자산)
- alias 점진 rename — 명명 일반화 + 점진 마이그레이션 안전 path
누적 (2026-05-15~17 전체)
- 머지 PR 55 + 4 = 59건
- 8394 web + 845 ops = 9239 테스트 통과
- 한 줄 본질 핵심 기능 누적:
- #17: Tenant RAG mock 자가 회귀
- #18: 사건 분류 + anomaly Phase 3 + production embedding eval (5/5)
- #19: Gemini classification eval + cron 가이드
- #20: Pack 6 형사 사건 first slice (14 → 15 종)
- #21: Pack 6.2 DocKind 22 등재 + CriminalInfoSection + RAG kind chart + exemplar v2
- #22: Pack 6.4+6.5+6.6 — 9 단계 흐름 + 정밀 시효 + 5 종 죄명 + exemplar cleanup
- #23: Pack 6.7+6.8 — dashboard portfolio + auto-stamp + docKind 어댑터 + CaseDomainType alias ← Pack 6 portfolio·생성 layer 완성
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인 (누적 4 세션 잔여)
- production Gemini classification baseline 측정 (real tenant 데이터)
- 공용 기본 형사 .docx 템플릿 3 종 작성 + Cloud Storage 업로드 (criminal-templates.md 가이드 따라)
- generateDocxAction 의 형사 docKind 분기 wiring — isCriminalDocKind 분기 후 buildCriminalDocxRenderData 호출
- Pack 6 dashboard widget 의 portfolio chart click → /cases?criminalPhase= 라우트 검증 (filter 사전 적용 동작)
- CaseDomainType 점진 sweep — 신규 PR 부터 alias 사용, 누적 호출부 갱신
- 형사 사건 portal 노출 정책 — 피해자/피의자 PII 노출 범위 ADR
2026-05-17 후속 #24 — Pack 6 production-ready 5 cap + 정합 cleanup
후속 #23 의 다음 세션 후보 6건 중 자율 모드 적합한 5건 일괄 ship — Pack 6 형사 도메인이 사용자 가시 흐름까지 모두 production. dev hydration · production Gemini baseline 2건은 외부 환경 의존으로 잔여.
머지 PR (5 commit + 1 fix, branch feat/pack6-docgen-wiring-and-pii-adr)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.9 docgen wiring | #2 generateCriminalDocxAction + UI 버튼 | thin wrapper Server Action (caseId + kind + attorney → buildCriminalDocxRenderData → generateDocxAction 위임). CriminalInfoSection 헤더에 "선임서·의견서·양형 자료" 3 버튼 추가. 다운로드 후 새 탭 시작 |
| Pack 6.7 filter | #3 /cases?criminalPhase= 필터 | RecoveryTypeFilter union 에 형사 5 종 추가. CriminalPhaseFilter 9 단계 신설. CasesListClient 가 URL 인자 hydrate. useCasesData 필터 분기 (legacy phase normalize 포함). CaseItem.recoveryType 19 종 + criminalPhase union 확장 |
| ADR 0046 | #5 형사 portal PII 정책 proposed | 3 정책 비교 (A noportal / B redacted / C opt-in). Policy A 권장 — 형사 PII 위험이 민사보다 압도적, 4자리 코드 인증 부적합, 1:1 변호인 채널이 형사 흐름에 더 적합. Pack 6.10 first slice 후속 작업 |
| Pack 6.11 templates | #1 공용 docx 템플릿 3 종 자동 생성 | scripts/generate-criminal-docx-templates.ts — JSZip + OOXML 직접 작성. Malgun Gothic + Title/Heading/Normal 3 스타일. 3 종 .docx (2.4 KB each) → apps/web/public/docxTemplates/. docx-templates createReport 로 sample data 렌더 검증 (placeholder 치환 0 잔존) |
| Pack 6 packages sweep | #4 packages/business-logic sweep | rag-eval/types.ts 의 LOCAL RecoveryType 14 종 → CaseDomainType 19 종 (Pack 6 sync) + RecoveryType @deprecated alias. 4 callsite (recovery-settlement · rag-eval/cases · rag-eval/index · types) sweep. apps/ops 62 ref · apps/web ~1700 ref 잔여 (자율 후속 PR) |
| docs broken-link fix | docusaurus build pass | criminal-templates.md 의 /docs/operations/public-template-playbook → 상대 경로 |
Ship 한 capability
- 3 형사 docKind 1-click 다운로드 — 변호인이 사건 메타 입력 후 헤더 버튼 한 번으로 docx. 사무소 자체 템플릿 업로드 시 자동 폴백
- dashboard chart → 필터 link — DashboardCriminalPortfolioCard 의 9 단계 chart 클릭이 실제로 사건 목록을 해당 단계로 필터. legacy phase 자동 normalize
- ADR 0046 정책 결정 path — 형사 portal 노출이 미정인 잔여 작업을 ADR 로 명시. Policy A (noportal) 권장 + closure path 정의
- 공용 기본 .docx 3 종 — repo 동봉 + 생성 스크립트 SSoT. 사무소 자체 작업 부담 0
- packages 형사 5 종 sync — RAG eval 골든 케이스 등록이 형사 사건도 수용 (이전엔 union mismatch 거부)
기술 결정
| 결정 | 이유 |
|---|---|
| .docx 를 JSZip + OOXML 직접 작성 | docx npm package 미설치, jszip 만 docx-templates 의 transitive dep. 최소 OOXML (Content_Types · _rels · word/document · styles) 직접 작성으로 외부 의존 0 |
| Policy A (noportal) 권장 | 형사 PII 위험 압도적 + 4자리 코드 인증 부적합. 사용자 피드백 후 점진 확장 path 명시 |
| RecoveryTypeFilter union 에 형사 5 종 추가 vs 별도 type | 단일 union 으로 dropdown UI 한 곳에서 처리 — 형사·민사 분리 dropdown 보다 단순 |
| CaseDomainType packages 먼저 sweep | 19 ref 만 — 작고 isolated. apps/ops 62 + apps/web 1700 보다 위험 ↓ |
| script 가 apps/web/public/ 직접 출력 | Cloud Storage 업로드 (ops) 와 분리 — repo 가 SSoT, Storage 는 cache. 다른 환경 deploy 도 자동 동기화 |
검증
- apps/web type-check 통과
- apps/ops type-check 통과
- apps/web 706 test files (8394 tests) 통과
- apps/ops 107 test files (845 tests) 통과
- 9239 누적 테스트 — drift 회귀 가드 유지
- docusaurus build 통과 (broken-link fix 포함)
- docx 렌더 sample —
홍길동·법무법인 너홀로·2026형제45678정확 치환,{{}}잔존 0 - 0 lint errors
한 줄 본질 정합
Pack 6 형사 도메인이 사용자 가시 흐름 (사건 등록 → 단계 입력 → 1-click docx 다운로드 → 다음 사건 사무소 학습) 까지 모두 production.
본 세션의 4 핵심 capability + 1 정합 cleanup 이 모두 한 줄 본질에 직접 기여:
- docgen wiring — 변호인이 형사 사건 메타 입력 후 1 클릭으로 docx 받음 = 사무소가 형사 사건 처리 마찰 감소
- dashboard filter — portfolio chart → 필터 사건 목록 = 변호인이 단계별 사건 collection 즉시 액세스
- 공용 템플릿 — 사무소 자체 작업 0 으로 형사 docx 생성 시작 가능
- portal PII ADR — 형사 데이터 노출 정책 결정 path 명시 = production-safe 환경 구축
- packages sync — RAG eval 골든 case 가 형사 사건도 학습 자산으로 등록 가능
누적 (2026-05-15~17 전체)
- 머지 PR 59 + 5 = 64건
- 9239 누적 테스트 통과
- 한 줄 본질 핵심 기능 누적:
- #17~#23 (선행 7 세션)
- #24: Pack 6 production-ready — docgen wiring + chart filter + ADR 0046 + 공용 docx + packages sync ← Pack 6 사용자 가시 흐름 closure
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인 (누적 5 세션 잔여 · 외부 환경)
- production Gemini classification baseline (real tenant 데이터)
- ADR 0046 Policy A 실 구현 (Pack 6.10) — portal 토큰 발급 시 criminal-* 거부 분기 + e2e
- CaseDomainType apps/ops 62 ref sweep (다음 자율 후보 — 위험도 ↓)
- Pack 6 docKind 렌더 결과 시각 검수 — 변호인이 docx 다운로드 후 한국 법무 양식 적합성
- 형사 사건 CriminalInfoSection 변호인 정보 자동 fetch (사무소 마스터 데이터 → attorney form 자동 채움)
- CriminalInfoSection 의 9 단계 timeline 클릭으로 단계 jump (현재는 select 만)
2026-05-17 후속 #25 — Pack 6 UX 폴리시 + Policy A ship (4 capability)
후속 #24 의 다음 세션 후보 7건 중 자율 모드 적합한 4건 (#3·#4·#6·#7) 일괄 ship. dev hydration · production Gemini baseline · 시각 검수 3 건은 외부 환경 의존으로 잔여.
머지 PR (4 commit, branch feat/pack6-policy-a-and-ux)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.10 Policy A | #3 ADR 0046 portal 거부 | createPortalToken 에 recoveryType ∈ criminal-* 거부 분기. PortalLinkDialog 가 props 로 recoveryType 받아 disabled Button "포털 (형사 비노출)" 렌더. 5 죄명 거부 + 민사 통과 + legacy 호환 회귀 가드 3 신규 테스트 |
| Pack 6.10 변호인 자동 fetch | #6 CriminalInfoSection attorney 자동 | getCriminalAttorneyDefaultsAction — tenant 마스터 데이터 우선순위 fetch (criminalDefaults > tenantName > empty). handleGenerate 가 defaults 받아 generateCriminalDocxAction.attorney 에 전달. 매번 input 부담 0 |
| Pack 6.10 timeline jump | #7 9 단계 click 시 phase jump | PhaseTimeline 각 segment 가 button 으로 변경 + onJump callback. handleJumpPhase — read 모드 즉시 저장 (autoStamp 일자 동시) · edit 모드 form sync. animate-pulse + "…" 라벨로 진행 시각 신호 |
| ops sweep | #4 apps/ops CaseDomainType sweep | run-add-rag-eval-case-scenario.ts · AddRagEvalCaseScenarioCard.tsx — local clone 14 → 19 종 (Pack 6 형사 5 추가). RecoveryType → CaseDomainType type rename. 이전 ops 시나리오에서 형사 골든 케이스 등록 거부되던 결함 해소 |
Ship 한 capability
- 형사 portal defense-in-depth 차단 — 서버 (createPortalToken) + UI (PortalLinkDialog disabled) 양쪽. 변호인이 실수로 형사 사건 토큰 발급 시도해도 거부
- 변호인 정보 마찰 0 — 사무소 setup 시 tenantName 만 입력해도 attorneyName 자동 사용. criminalDefaults 설정 시 형사 전용 프로필 사용
- timeline = action surface — read 모드의 9 단계 timeline 이 단순 viewing 에서 액션 가능 컨트롤로. 변호인이 사건 진척을 timeline 1 클릭으로 완료
- ops 골든 케이스 형사 수용 — RAG eval 골든 셋이 Pack 1~6 19 종 모두 등록 가능
기술 결정
| 결정 | 이유 |
|---|---|
| Policy A 서버 + UI 양쪽 차단 (defense-in-depth) | UI 만 disabled 면 직접 Server Action 호출로 우회 가능. 패키지 함수에 검사 추가로 모든 경로 차단 |
| PortalLinkDialog props.recoveryType 옵셔널 | 기존 호출부 (Pack 1~5) 호환 — recoveryType 미전달 시 정상 dialog 동작 |
| getCriminalAttorneyDefaults 우선순위 (criminalDefaults > tenantName > empty) | 사무소가 별도 형사 프로필 설정 안 했어도 정상 동작 (tenantName fallback). source 필드로 fetch 경로 노출 — debugging 가능 |
| timeline click = 즉시 저장 (read 모드) | 변호인 워크플로: timeline 보다가 단계 진척 클릭 — 추가 confirm 모달 없이 즉시 commit + toast 안내. 잘못 클릭 시 이전 단계 다시 클릭으로 되돌릴 수 있음 |
| Edit 모드 timeline click = form 변경만 | 사용자가 편집 중일 때는 timeline click 도 form-only — 저장 버튼이 single commit point |
| ops local clone 명시적 sync | apps/* 간 import 금지로 ops 가 자체 enum 필요. 14 → 19 종 명시 갱신 (ADR 0028 alias 사용 불가 — packages 의 alias 도 ops 입장에선 동일 cost) |
검증
- apps/web type-check 통과
- apps/ops type-check 통과
- apps/web 706 test files (8397 tests) 통과 (+3 신규 Policy A)
- apps/ops 107 test files (845 tests) 통과
- 0 lint errors
한 줄 본질 정합
Pack 6 형사 도메인이 보안 (portal 거부) + UX 마찰 (변호인 자동 fetch + timeline 직접 액션) 면에서 production polish.
본 세션의 4 capability 모두 한 줄 본질의 핵심 보강:
- Policy A — 사무소 데이터 (형사 PII) 가 의뢰인 외부로 누출되지 않는 default-safe 보안 layer. 한 줄 본질 의 "자기 데이터" 가 사무소 내부에 머무름을 보장
- 변호인 자동 fetch — 사무소가 변호인 정보 매번 입력하지 않아도 docx 생성 — 데이터 입력 마찰이 학습 자산 누적 속도 ↑
- timeline jump — 변호인이 사건 진척을 즉시 stamp — 사무소 형사 portfolio 데이터 quality ↑
- ops sync — RAG eval 골든 셋이 형사 사건도 학습 자산으로 검증 — 사무소 AI 학습 회귀 측정 surface 가 19 종 전체 cover
누적 (2026-05-15~17 전체)
- 머지 PR 64 + 4 = 68건
- 9242 누적 테스트 통과 (web 8397 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#24 (선행 8 세션)
- #25: Pack 6 UX polish + Policy A ← 형사 도메인 보안·UX 표면 정착
다음 세션 후보
- dev cold restart 후 hydration 결함 시각 재확인 (누적 6 세션 잔여 · 외부)
- production Gemini classification baseline (real tenant 데이터)
- Pack 6 docKind 렌더 결과 시각 검수 (변호인 1:1 review · 외부)
- 사무소 settings 에 criminalDefaults 편집 UI (Pack 6.12) — attorneyName/address/number 사무소 단위 설정
- useTenant Provider 가 criminalDefaults 도 expose (client cache · refresh 시 1 fetch 절감)
- CaseDomainType apps/web ~1700 ref 점진 sweep (다음 자율 후보)
- timeline jump 의 backwards click (예: trial-first → investigation) 사용자 confirm 모달 (실수 보호) → v1 호환 필드 제거
2026-05-17 후속 #26 — Pack 6.12 사무소 설정 + UX 정밀화 (4 capability)
후속 #25 의 다음 세션 후보 7 건 중 자율 가능 4건 (#4 + #5 묶음, #6, #7) 일괄 ship. 외부 환경 의존 3 건 (#1, #2, #3) 잔여.
머지 PR (3 commit + 1 sweep)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.12 settings + cache | #4+#5 형사 변호인 기본 프로필 + TenantProvider 캐시 | saveCriminalDefaultsAction + CriminalDefaultsSection (owner only · 길이 검증 · 빈 값 FieldValue.delete). (workspace)/layout.tsx 가 tenant fetch 시 criminalDefaults 도 읽어 TenantProvider context 캐시. CriminalInfoSection 이 useTenant() 로 캐시 사용 — 매 다운로드마다 호출되던 getCriminalAttorneyDefaultsAction 1 회 절감 |
| Pack 6.12 backward confirm | #7 backward jump AlertDialog | PhaseTimeline click 이 backward (현재 → 이전 단계) 인 경우 AlertDialog 확인 모달. Forward 클릭은 그대로 즉시 commit (자연스러운 워크플로). commitPhaseJump 함수 분리 — 모달 확인 후 호출 |
| CaseDomainType sweep #3 | #6 apps/web partial sweep | criminal-portfolio.ts (Pack 6.7) + set-recovery-type-action.ts type identifier 만 RecoveryType → CaseDomainType. function 이름 + log 라벨 유지 (callsite 호환) |
Ship 한 capability
- 사무소 단위 형사 변호인 프로필 — owner 가 settings 에서 1 회 입력하면 모든 형사 사건 자동 적용. 매 사건 마다 wizard 가 attorney 묻지 않음
- TenantProvider context 캐시 — layout SSR 시 1 fetch (logoUrl 과 동일 snapshot 활용). client 컴포넌트는 useTenant() 한 줄로 즉시 사용. Server Action 절감 (3 docKind 클릭 시 3 → 0)
- backward jump 실수 보호 — Forward 는 매끄럽게, backward 는 명시적 confirm. 실수로 phase 되돌리는 사고 차단
- 점진 sweep 3 차 — packages (session #24) + ops (session #24) + web partial (이번 세션) 누적
기술 결정
| 결정 | 이유 |
|---|---|
| layout.tsx 에 criminalDefaults fetch 합치기 | 이미 logoUrl 위해 tenant doc 조회 중 — 같은 snapshot 에서 한 번에 읽으면 fetch 0 비용 추가 |
| useTenant().criminalDefaults 옵셔널 | 설정 미입력 사무소 호환 (undefined). 기존 호출부 (Pack 1~5) 영향 0 |
| FieldValue.delete() 빈 값 처리 | Firestore 에 "" 저장 안 함 — undefined vs "" 분기 모호함 회피 |
| Forward 즉시 commit · Backward 만 confirm | 변호인의 자연 워크플로 (다음 단계 진척) 는 마찰 0, 실수 발생할 만한 동작 (역방향) 만 명시 confirm |
| 점진 sweep — 함수 이름 유지 | RecoveryTypeControls / setCaseRecoveryTypeAction 같은 callsite 의존 식별자는 보존. type 식별자만 rename — diff 최소화 |
검증
- apps/web type-check 통과
- apps/web 706 test files (8397 tests) 통과
- 0 lint errors
한 줄 본질 정합
사무소 형사 도메인 운영 마찰 ↓ + 실수 보호 layer + 명명 점진 정착.
본 세션 4 commit 모두 사무소가 형사 사건을 더 빠르고 안전하게 처리하도록:
- criminalDefaults settings — 사무소가 형사 변호인 정보 1회 입력하면 모든 후속 사건 자동 적용 = 변호인 데이터 capture 마찰 ↓
- TenantProvider cache — 매 docgen 클릭마다 Server Action 절감 = response time ↓
- backward confirm — 실수로 단계 되돌리는 사고 방어 = 데이터 quality ↑
- partial sweep — Pack 6 형사 5 종 추가 후 명명이 자연스러워지는 path
누적 (2026-05-15~17 전체)
- 머지 PR 72 + 4 = 76건
- 9242 누적 테스트 통과 (web 8397 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#25 (선행 9 세션)
- #26: Pack 6.12 사무소 설정 + UX 정밀화 + sweep #3 ← Pack 6 운영 안정 layer
다음 세션 후보
- dev cold restart hydration · production Gemini baseline · 시각 검수 (외부 환경 3 건 누적)
- CaseDomainType apps/web ~1700 ref 자동화 sweep (codemod / sed 기반)
- CriminalDefaultsSection 의 e2e — owner 권한 / staff 권한 / 빈 입력 시나리오
- timeline forward click 시 1-tap 단계 진척 UX 검증 (e2e)
- Pack 6 의 portal 사이드바 hide — 의뢰인 portal 이 형사 사건 자체 404 처리 (ADR 0046 Policy A 후속 layer)
- 형사 사건 case-flow status badge — 9 단계 → 6 단계 case-flow SSoT mapping
- CriminalInfoSection 의 변호사 본인 (session.name) vs criminalDefaults 분리 — 사무소 대표 vs 담당 변호사
2026-05-17 후속 #27 — Pack 6.13 Policy A 도착 layer + case-flow 매핑 + 단위 가드 (4 capability)
후속 #26 의 다음 세션 후보 7 건 중 자율 가능 4건 (#5 → portal layer / #6 → case-flow / #3 → CriminalDefaults 단위 / #2 → sweep 추가) 일괄 ship. 외부 환경 의존 3 건 (#1·#4 e2e·시각 검수) 잔여.
머지 PR (4 commit, branch feat/pack6-portal-hide-and-flow)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6 portal layer | #5 ADR 0046 Policy A 도착 시점 차단 | validateTokenAction 에 case doc fetch + criminal-* 거부 분기. defense-in-depth 2 layer (발급 + 사용). legacy 토큰 / 사후 recoveryType 변경 / 발급 우회 시도 모두 차단. fail-safe close (case 조회 실패 시 거부). 3 신규 테스트 |
| Pack 6.13 case-flow 매핑 | #6 9 단계 → 5 단계 case-flow status | mapCriminalPhaseToStatus pure function — 변호인 실무 시각 매핑 (defense-appointed→소제기, investigation~appeal→진행중, supreme-appeal→판결대기, finalized→종결, enforced→집행중). CriminalInfoSection 헤더에 STATUS_STYLE 재사용 badge 추가. 7 신규 테스트 |
| CriminalDefaults 단위 | #3 validate 순수 함수 분리 + 13 단위 테스트 | settings/_lib/criminal-defaults-validate.ts pure function. settings/_actions 가 inline validate 제거하고 import. 빈 문자열 허용 / null·undefined·string 거부 / 3 boundary inclusive 통과 검증 |
| CaseDomainType sweep #4 | #2 점진 sweep — icon-map + statute-of-limitations | type identifier 만 RecoveryType → CaseDomainType (function 이름 + 합성 식별자 보존). case-recovery-type.ts sed 충돌로 revert — function name resolveCaseRecoveryType 보호 |
Ship 한 capability
- portal Policy A 2 layer 차단 — 발급(#2355)+사용(이번) 두 시점 모두 criminal-* 거부. legacy 토큰 + 사후 변경 + 우회 시도 모두 보호
- case-flow 일관성 — 형사 사건이 dashboard "사건 단계 분포" 등 공용 위젯과 동일 status badge 사용 가능. 9 단계 phase 의 진행도가 5 단계 status 로 자연 mapping
- CriminalDefaults 회귀 가드 — Server Action 검증 로직 분리 + 13 단위 테스트. mock 없이 validation 회귀 자동 catch
- 점진 sweep 4 차 — 누적 4 sweep (packages 19, ops 62, web partial #1 7, web partial #2 9 ref)
기술 결정
| 결정 | 이유 |
|---|---|
| validate 시점에 case fetch | 토큰 발급 시 ✓ + 사용 시 ✓ defense-in-depth. fail-safe close 로 일시 장애 시 안전 우선 |
| mapCriminalPhaseToStatus pure function | UI badge 외 향후 dashboard 분포 widget 등 재사용 가능. exhaustive switch — Pack 6 phase 추가 시 빌드 타임 catch |
| STATUS_STYLE 재사용 | 형사 status badge 가 민사와 시각적으로 동일 — case-flow 단일 surface 유지 |
| validate function 분리 후 export type alias | callsite 호환 (CriminalDefaultsPayload import 경로 동일) |
| case-recovery-type.ts sed revert | function name resolveCaseRecoveryType 충돌 — 안전한 sweep 만 commit (sed regex 한계) |
검증
- apps/web type-check 통과
- apps/web 707 test files (8420 tests) 통과 (+23 신규 — Policy A 3 + case-flow 매핑 7 + validate 13)
- 0 lint errors
한 줄 본질 정합
사무소 형사 도메인 보안 (2 layer) + 도메인 일관성 (case-flow) + 회귀 가드 (단위 테스트) 강화.
본 세션 4 capability 모두 한 줄 본질 운영 layer 보강:
- portal 2 layer — 사무소 형사 PII 가 의뢰인 portal 로 누출되지 않음을 발급+사용 시점 모두 보장
- case-flow 매핑 — 형사 사건도 dashboard·portfolio·status 분포 위젯에서 다른 도메인과 일관된 surface
- validate 분리 — 검증 로직 회귀 위험 ↓ — 향후 Pack 6.X 도메인 확장 시 안전 path
- 점진 sweep — 명명 정착 안정성 — 신규 PR 이 새 이름 사용하는 기준 marker 누적
누적 (2026-05-15~17 전체)
- 머지 PR 76 + 4 = 80건
- 9265 누적 테스트 통과 (web 8420 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#26 (선행 10 세션)
- #27: Pack 6.13 Policy A 2 layer + case-flow 매핑 + 단위 가드 ← Pack 6 운영 안정 layer 정착
다음 세션 후보
- dev cold restart hydration · production Gemini baseline · 시각 검수 (외부 환경 3 건 누적)
- e2e Policy A — 형사 사건 발급 거부 + portal 도착 거부 시나리오 (외부 환경)
- e2e CriminalDefaultsSection — owner 입력·staff 차단·empty 시나리오 (외부 환경)
- dashboard "사건 단계 분포" 위젯에 Pack 6 형사 사건도 카운트 (case-flow 매핑 활용)
- CaseDomainType apps/web 잔여 30 파일 자동화 sweep (codemod 도구)
- 형사 사건 case-flow 진행률 시각화 widget — overview 탭 별도 카드
- ADR 0046 후속 — Policy B (redacted portal) 또는 C (opt-in) 의 사용자 피드백 baseline 측정 가이드
2026-05-17 후속 #28 — Pack 6.14 dashboard 통합 + sweep 5 차 + ADR 0047 (3 capability)
자율 모드 — 후속 #27 의 다음 세션 후보 7건 중 자율 가능 3건 일괄 ship. 외부 환경 4건 잔여.
머지 PR (3 commit, branch feat/pack6-dashboard-flow-widget)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.14 dashboard | #1 case-flow distribution 형사 통합 | inferCaseFlowStage 에 criminalPhase 시그널 + mapCriminalPhaseToFlow pure 매핑 (9 → 6 단계). CaseFlowDistributionCard 가 d.criminalPhase 전달 → dashboard "사건 단계 분포" 위젯이 민사+형사 통합 카운트. 12 신규 단위 테스트 |
| sweep 5 차 | #5 perl word boundary sweep | 12 추가 파일 RecoveryType → CaseDomainType (lib · _recovery · _tests). function name + 합성 식별자 모두 보존 |
| ADR 0047 | #4 Policy 진화 baseline | Pack 6.10 (Policy A) 의 6개월 운영 후 Policy B/C 채택 평가 기준. 3 측정 항목 + 결정 트리 + B/C 구현 가이드 |
Ship 한 capability
- dashboard 형사 사건 통합 — CaseFlowDistributionCard 가 9 단계 형사 phase 도 6 단계 case-flow 로 mapping → 단일 위젯에서 민사+형사 portfolio 한눈 (회수 사무소·형사 사무소 모두 활용)
- 명명 정착 누적 진척 — packages 19 + ops 62 + web (sweep #1~5) ~50 ref 자율 마이그레이션
- Policy A 평가 path — 사용자 피드백 baseline 정의로 ADR 0046 의 "6개월 후 평가" closure path 명시
기술 결정
| 결정 | 이유 |
|---|---|
| criminalPhase 우선 매핑 (다른 휴리스틱 무시) | 형사 사건은 hearingCount·judgmentDate 등 일반 시그널과 의미 다름. phase 자체가 SSoT |
| 9 → 6 매핑 (변호인 워크플로) | case-flow.md SSoT 의 의미 보존 — 분류/서류/제출/재판/결과/집행 |
| perl negative lookbehind sweep | function name (resolveCaseRecoveryType) + 합성 식별자 보존. BSD sed 한계 우회 |
| Policy B/C 구현 보류 (ADR draft) | production 측정 데이터 없으면 채택 근거 모호 — measurement-first |
검증
- apps/web type-check 통과
- apps/web 707 test files (8432 tests) 통과 (+12 신규)
- 0 lint errors
- docusaurus build 통과 (ADR 0047 포함)
한 줄 본질 정합
Pack 6 형사 도메인이 dashboard 통합 표면 + 명명 정착 + 정책 진화 path 까지 layer 안정.
본 세션의 3 capability 모두 사무소 운영 surface 의 일관성·진화 path 강화:
- dashboard 통합 — 형사 사무소도 case-flow 위젯 활용 가능 (이전엔 형사 사건이 별도 위젯 only)
- sweep 진척 — 신규 PR 이 새 이름 사용하는 누적 marker. 향후 ADR 0028 closure 의 베이스
- ADR 0047 — 미래 policy 결정에 측정 데이터 기반 평가 path 명시
누적 (2026-05-15~17 전체)
- 머지 PR 80 + 3 = 83건 (이번 세션은 squash 1)
- 9277 누적 테스트 통과 (web 8432 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#27 (선행 11 세션)
- #28: dashboard 형사 통합 + sweep 5 + ADR 0047 ← Pack 6 표면 정합 + 진화 path
다음 세션 후보
- ops dashboard 의 Pack 6 portal 측정 위젯 (ADR 0047 자동 수집 Pack 6.16)
- case-flow distribution chart 의 형사 case 클릭 → /cases?recoveryType=criminal-* filter
- CaseDomainType apps/web 잔여 ~15 파일 sweep 6 차
- 형사 사건 단계 별 elapsed days 텔레메트리 (변호인 portfolio 시각)
- 외부 환경 의존 누적 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-17 후속 #29 — Pack 6.15 + 6.16 + sweep 6 차 (3 capability)
자율 모드 — 후속 #28 의 다음 세션 후보 5건 중 자율 가능 3건 ship. 외부 환경 1 + ops 위젯 1 잔여.
머지 PR (3 commit, branch feat/pack6-sweep6-and-elapsed)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| sweep 6 차 | CaseDomainType 20 추가 파일 마이그레이션 | perl word boundary 로 lib · _recovery · _tests · legacy/v2 (20 파일). compatibility 문제로 recovery-input-schemas.ts · new/_components revert |
| Pack 6.15 chart click filter | #2 case-flow distribution row click → /cases?caseFlowStage=X | CaseFlowStageFilter 9 단계 union + CaseFilterState.caseFlowStage. CasesListClient hydrate + useCasesData filter (inferCaseFlowStage 동일 알고리즘). CaseFlowDistributionCard 각 row Link wrap |
| Pack 6.16 elapsed days | #4 단계별 경과일 텔레메트리 | computeCriminalElapsed pure function — totalDays · daysAtCurrentPhase · daysSinceIndictment. ElapsedRow 컴포넌트 — 90 일+ amber 강조. 8 신규 단위 테스트 |
Ship 한 capability
- 누적 ~150 ref 명명 마이그레이션 — packages 19 + ops 62 + web sweep #1~#6 ≈ 70
- dashboard → 사건 목록 single click filter — 변호인이 단계 row 1 클릭으로 해당 단계 사건 목록 즉시 액세스 (Pack 6 형사 + Pack 1~5 민사 동일 UX)
- stuck 패턴 시각 — 변호인이 사건이 같은 단계에 90 일+ 머무는 것 즉시 catch. portfolio 시각의 "처리 속도" baseline
기술 결정
| 결정 | 이유 |
|---|---|
| sweep revert 일부 | recovery-input-schemas.ts 의 CaseDomainType export alias 자체 충돌 + Step1RecoveryType filename callsite 의존. 부분 revert 로 안정 우선 |
| useCasesData 의 caseFlowStage 분기에서 inferCaseFlowStage 재호출 | CaseFlow 위젯과 동일 알고리즘 보장 (drift 0). 200 case 한도 성능 영향 미미 |
| 90 일 stuck threshold | 형사 사건 단계 평균 처리 일수의 보수적 임계 — production 데이터 누적 후 조정 후보 |
| ElapsedRow 일자 모두 null 시 자체 hide | 새 등록 사건 / 미입력 케이스 시각 노이즈 회피 |
검증
- apps/web type-check 통과
- apps/web 707 test files (8440 tests) 통과 (+8 신규)
- 0 lint errors
한 줄 본질 정합
Pack 6 형사 운영 시각 강화 (chart filter · stuck pattern) + 명명 정착 누적.
본 세션 3 capability 모두 한 줄 본질의 운영 표면:
- chart filter — 변호인이 사무소 portfolio 를 single click 으로 detailed list 액세스 가능
- elapsed days — 사무소 "처리 속도" baseline. 향후 production 데이터 누적 시 stuck pattern 자동 detection
- sweep 6 차 — Pack 6 + ADR 0028 명명 정착 marker
누적 (2026-05-15~17 전체)
- 머지 PR 83 + 3 = 86건 (squash 1)
- 9285 누적 테스트 통과 (web 8440 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#28 (선행 12 세션)
- #29: chart filter + elapsed days + sweep 6 ← Pack 6 portfolio 시각 UX 정합
다음 세션 후보
- ops dashboard 의 Pack 6 portal 측정 위젯 (ADR 0047 활동 로그 type=portal_token_blocked_criminal 신설 + 자동 수집 Pack 6.17)
- CriminalInfoSection elapsed days 의 production tenant 데이터 baseline (외부)
- case-flow chart click 의 e2e (외부)
- CaseDomainType apps/web 잔여 ~5 파일 sweep 7 차 (recovery-input-schemas 본체 외)
- 외부 환경 의존 누적 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-17 후속 #30 — Pack 6.17 activity log + 6.18 ops 위젯 (2 capability)
자율 모드 — 후속 #29 의 다음 세션 후보 5건 중 자율 가능 2건 ship (큰 비중). 외부 환경 3건 잔여.
머지 PR (2 commit, branch feat/pack6-ops-portal-metrics)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.17 activity type | portal_token_blocked_criminal 신설 | ActivityType union + writeActivityLog 호출 (workspace · portal 양쪽). logCriminalPortalBlock helper 분리 (200줄 invariant 보호). ActivityLogClient label/tone + activity-notification-bridge 매핑 |
| Pack 6.18 ops 위젯 | dashboard 형사 portal 거부 측정 | Pack6PortalBlockedCard — collectionGroup 30일 누적 + 발급/사용 분기 + top 5 tenants. 활동 0 건 자체 hide. ADR 0047 baseline 자동 수집 |
Ship 한 capability
- ADR 0046 Policy A 텔레메트리 — 발급+사용 시점 거부가 활동 로그로 영구 기록. 의뢰인 알림은 보내지 않음 (audit only)
- ops baseline 자동 수집 — ADR 0047 의 6 개월 평가 측정이 production 부터 누적 시작. 운영자가 dashboard 에서 즉시 확인
- 명명 누적 진척 — sweep 7 차 검토 (대부분 function name + 식별자로 자연 보호, 9 파일만 type 임포트 잔여)
기술 결정
| 결정 | 이유 |
|---|---|
| logCriminalPortalBlock helper 분리 | 200줄 baseline invariant 통과 + 두 호출부 동일 로직 sync |
| activity log type 신설 vs metadata 분기 | 운영 dashboard 가 type filter 로 즉시 count — metadata 검색은 collectionGroup 성능 ↓ |
| 활동 0 건 자체 hide | 대부분 사무소 (비형사) 노이즈 회피. Pack 6 도입 사무소만 위젯 노출 |
| activity-notification-bridge: null | 의뢰인에게 거부 사실 알림 발송 안 함 (PII 안전) |
검증
- apps/web type-check + 707 tests (8440) 통과
- apps/ops type-check + 107 tests (845) 통과
- 0 lint errors
- server-action 200줄 baseline 갱신 (portal-token-actions 212)
한 줄 본질 정합
형사 PII 보호 ↑ + 정책 진화 측정 자동화 + 명명 정착 누적.
본 세션 2 commit 모두 한 줄 본질의 운영 layer:
- activity log telemetry — 사무소 정책 효과를 데이터로 측정 (ADR 0047 결정 트리 자동 입력)
- ops 측정 위젯 — 운영자가 production 신호를 실시간 확인 (수동 sample 0)
누적 (2026-05-15~17 전체)
- 머지 PR 86 + 2 = 88건 (squash 1)
- 9285 누적 테스트 통과
- 한 줄 본질 핵심 기능 누적:
- #17~#29 (선행 13 세션)
- #30: activity log telemetry + ops 위젯 ← Pack 6 정책 진화 측정 자동화
다음 세션 후보
- ops Pack 6 위젯의 e2e + 활동 로그 시드 (외부 환경)
- 사무소 dashboard 의 "Pack 6 portal 거부" 카운트 표시 (workspace 측 mirror)
- CaseDomainType sweep 8 차 —
lib/cases/schemas.tsre-export 확장 + 9 잔여 파일 - case-flow 의 "stuck pattern" 자동 anomaly 가설 trigger (ADR 0042 통합 baseline)
- 외부 환경 의존 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-18 후속 #31 — Pack 6.19 stuck count + 6.20 anomaly trigger + sweep 8 (3 capability)
자율 모드 — 후속 #30 의 다음 세션 후보 5건 중 자율 가능 3건 ship.
머지 PR (3 commit, branch feat/pack6-mirror-and-stuck-trigger)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.19 stuck count | portfolio stuck 사건 카운트 | computeCriminalPortfolio.stuckCount + DashboardCriminalPortfolioCard 의 amber chip "stuck N (90일+)". 5 신규 단위 테스트 (90 임계 inclusive + finalized 제외 + 분포) |
| sweep 8 차 | schemas.ts Pack 6 sync + CaseDomainType alias | lib/cases/schemas.ts 14 → 19 종 RECOVERY_TYPE_VALUES (Pack 6 형사 5 추가). NON_DEBT_RECOVERY_TYPES 8 → 13. CaseDomainType + CASE_DOMAIN_TYPE_VALUES alias 신설. types/case.ts CasePayload union 동기화 |
| Pack 6.20 anomaly trigger | ADR 0042 stuck 시그널 통합 | criminalStuckSignal schema + readCriminalStuckSignal collector + classifyCriminalStuckStatus + prompt-builder formatSignalLine/extractAnomalyKeywords. LLM 가설 자동 trigger (30%+ red · 15~30% yellow). 9 신규 단위 테스트 |
Ship 한 capability
- stuck pattern 3 층 모니터링 — Pack 6.16 (사건 단위 elapsed) + 6.19 (portfolio chip) + 6.20 (anomaly LLM 가설). 변호인 시각 + 사무소 portfolio 시각 + 운영 anomaly catch
- schemas Pack 6 sync — 신규 사건 wizard Step3 가 형사 5 종 정상 수용 (이전 silent skip 위험 해소)
- 명명 정착 누적 진척 — packages 19 + ops 62 + web sweep #1~#8 ≈ 90+ ref 마이그레이션
기술 결정
| 결정 | 이유 |
|---|---|
| stuckRatio 30/15% 임계 | 보수적 시작점. ADR 0047 baseline 같이 production 측정 후 조정 |
| ADR 0042 signal collector 에 통합 | LLM 가설 자동 trigger — 운영자가 수동 검사 불필요 |
| finalized/enforced 제외 | 종결 사건은 stuck 무관 (재판 종결 = 정상 진행) |
| classifyCriminalStuckStatus pure | bootstrap 분류로 형사 미운영 사무소 noise 제거 |
검증
- apps/web type-check + 707 test files (8469 tests) 통과 (+14 누적)
- 0 lint errors
한 줄 본질 정합
사무소 형사 portfolio 의 "처리 속도" anomaly 가 LLM 가설로 자동 surface.
본 세션 3 capability 모두 anomaly 자동화 closed-loop 강화:
- stuck count — portfolio 수준 시그널 (사무소가 사건이 stuck 됐는지 한눈에)
- schemas sync — Pack 6 데이터 무결성 (wizard 누락 fix)
- anomaly trigger — operations layer 자동화 (운영자 부담 ↓ · 사무소 운영 anomaly 자동 detect)
누적 (2026-05-15~18 전체)
- 머지 PR 88 + 3 = 91건 (squash 1)
- 9314 누적 테스트 통과 (web 8469 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#30 (선행 14 세션)
- #31: stuck pattern 3 층 monitoring + anomaly LLM trigger + schemas sync ← Pack 6 운영 anomaly closed-loop
다음 세션 후보
- Pack 6 형사 사건 별도 시드 fixture (tenant-fixture page 의 1 클릭 시드 옵션)
- CriminalDefaultsSection production 검증 e2e (외부 환경)
- Pack 6.21 — 형사 사건 timeline 9 단계 별 ETA 산출 (사무소 별 평균 처리 일수)
- CaseDomainType sweep 9 차 — recovery-input-schemas 본체 (CaseDomainType primary, RecoveryType deprecated)
- 외부 환경 의존 5건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-18 후속 #32 — Pack 6 형사 시드 fixture + anomaly summary 통합 (1.5 capability)
자율 모드 — 후속 #31 의 다음 세션 후보 5건 중 자율 가능 1건 + 부수 fix 1건 ship.
머지 PR (1 commit, branch feat/pack6-seed-and-eta)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6 시드 fixture | #1 ops 1 클릭 시드 + signalToSummary 통합 | seed-criminal-portfolio-mock-action.ts — 사기 3 + 횡령 2 + 폭행 1 (가중·구속·시효임박·stuck 다양 분포). UI 추가 (tenant-fixture page red 강조 섹션). anomaly-hypothesis-helpers.ts 의 signalToSummary 가 criminalStuck case 추가 (sourceMetric=criminalStuck.ratio) |
Ship 한 capability
- Pack 6 emulator dev path — 형사 사건 6 건 1 클릭으로 dashboard + ops 위젯 + anomaly 가설 모두 검증 가능. tenant-fixture page 에 Anomaly mock 와 같은 UX 패턴 통합
- anomaly summary 완성 — signalToSummary 가 4 source 모두 cover (ragMergeQuality + docgenStats + draftDiffs + criminalStuck). 가설 카드 표시 시 criminalStuck 도 일관
기술 결정
| 결정 | 이유 |
|---|---|
| 시드 사건 6 건 mix | 모든 alert chip + stuck pattern + anomaly trigger 검증할 수 있는 최소 분포 |
| signalToSummary 누락 분기 채움 | Pack 6.20 signal 추가 후 helper 가 noReturn 컴파일 에러로 노출 — 4 case discriminated union exhaustive 강제 |
| sweep 9 차 skip | alias 상태 이미 최적 — RecoveryType ↔ CaseDomainType 양방향 호환 + neue 코드는 새 이름 사용 |
| ETA 산출 보류 | 9 단계 평균 처리 일수는 사무소 별 데이터 누적이 필요한 baseline. Pack 6.20 anomaly trigger 가 stuck pattern 으로 이미 동일 신호 cover. 별도 위젯의 가치 명확하지 않아 보류 |
검증
- apps/ops type-check + 107 test files (845 tests) 통과
- apps/web 무관 회귀 0
한 줄 본질 정합
Pack 6 형사 도메인 production-ready validation 자동화 — dev path 마찰 0 + anomaly summary 일관성.
본 세션 1 capability 가 사무소 운영 검증 surface 보강:
- dev path 시드 — production 검증 없이 모든 Pack 6 UX 검증 가능
- summary 통합 — Pack 6.20 anomaly trigger 가 LLM 가설 layer 까지 완전히 통합 (sourceMetric=criminalStuck.ratio 가 hypothesis.signals 에 정상 표시)
누적 (2026-05-15~18 전체)
- 머지 PR 91 + 1 = 92건 (squash)
- 9314 누적 테스트 통과 (web 8469 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#31 (선행 15 세션)
- #32: Pack 6 시드 fixture + anomaly summary 통합 ← Pack 6 dev validation 표면
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 9 단계 평균 처리 일수 ETA 산출 — 사무소 별 historical 데이터 baseline 후 추가
- CaseDomainType apps/web 잔여 ~9 파일 sweep — recovery-input-schemas alias swap (RecoveryType deprecated 표시)
- Pack 6.22 — anomalyHypothesis 카드의 criminalStuck source 별 추천 action ("부담 변호인 추가" 등)
- 외부 환경 의존 5건
2026-05-18 후속 #33 — Pack 6.22 anomaly action library + sweep 9 canonical swap (2 capability)
자율 모드 — 후속 #32 의 다음 세션 후보 5건 중 자율 가능 2건 (#3 sweep 9 · #4 Pack 6.22) 일괄 ship. 외부 환경 의존 3 건 잔여.
머지 PR (2 commit, branch feat/pack6-anomaly-actions-sweep9)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.22 anomaly action library | #1 LLM + curated 액션 merge | packages/business-logic/src/ops-anomaly/suggested-actions.ts (NEW) — suggestedActionsForSource(source, status) 순수 함수 라이브러리. criminalStuck red → 3 액션 (stuck 검토 · 변호인 분담 · 인력 보강), yellow → 1 액션 (progress jump). ragMergeQuality / docgenStats / draftDiffs 도 status 별 1 액션. mergeSuggestedActions(llm, suggested, max=5) 가 LLM 우선 + 중복 label 제거 + max cap. generateAnomalyHypothesisAction 가 LLM proposedActions + curated merge → Firestore 저장. 12 unit tests |
| ADR 0028 sweep 9 — canonical swap | #2 RecoveryType → CaseDomainType 정식 승격 | 두 canonical 파일 (apps/web/lib/cases/schemas.ts + recovery-input-schemas.ts) 의 canonical 을 CASE_DOMAIN_TYPES / CaseDomainType 으로 swap. RECOVERY_TYPES / RecoveryType / RECOVERY_TYPE_LABEL / RecoveryTypeSchema 는 alias 로 보존 + @deprecated JSDoc (IDE 경고). drift 가드 신설 — case-domain-type-parity.test.ts 19 종 순서·내용 + alias referentially 검증 (7 tests). 1785+ 참조 호출부 동작 무변화 |
Ship 한 capability
- anomaly LLM 카드 행동 가능 보장 — LLM 응답에 source-specific 액션 누락 시 curated 라이브러리가 자동 보강. criminalStuck red 가설 카드에서 변호사가 즉시 액션 가능 (사건 목록 검토 → /cases?caseFlowStage=5 직링크 + 분담 검토 + 인력 보강). 4 source × 2 status 매트릭스 cover
- CaseDomainType naming 정식 승격 — Pack 1 시점 명명 (RecoveryType) 이 Pack 1~6 19 종 도메인 전체로 일반화된 의미를 IDE 가 가르치게 됨.
@deprecated가 신규 코드의 자연스러운 migration 유도. drift 가드가 sweep 회귀 차단
기술 결정
| 결정 | 이유 |
|---|---|
| curated 액션은 pure function library 분리 | LLM 응답이 source 별 액션 누락 시 보강 — ops-anomaly 패키지에 두면 generate-anomaly-hypothesis-action.ts + future ops UI 모두 재사용. PR-B6 server action 의 helper 책임 분리 |
| LLM 액션을 우선시키고 curated 는 fallback | LLM 은 시그널 context 보고 더 구체적 액션 생성 가능. curated 는 그 source 의 minimum-viable 액션 보장 |
| max cap 5 적용 | 카드 UI 가 5 개 액션 이상 표시하면 운영자 cognitive load 폭증. 중복 dedup 도 일관 라벨 가정 |
| scenarioCardId 의무 X | curated 일부에만 부착 (case-flow-stuck-review · rag-self-regression-run). LLM 이 자유롭게 scenarioCardId 제안 가능하도록 자리 남김 |
| canonical swap 실시 (RecoveryType alias 보존) | 1785+ 참조 호출부 break 비용 회피 — alias referentially 동일하므로 동작 0 변화. IDE @deprecated 가 점진 sweep 유도 |
| drift 가드 추가 | 두 canonical 파일이 같은 19 종 정의를 별도 const 로 유지 — drift 시 silent 회귀 위험 (wizard Step1 schema 가 schemas.ts 사용, 나머지 도메인 입력은 recovery-input-schemas.ts 사용). parity test 가 순서·라벨 ·alias 동등성 모두 강제 |
검증
- apps/web type-check + 708 test files (8481 tests) 통과
- apps/ops type-check + 107 test files (845 tests) 통과
- 신규 12 + 7 = 19 단위 테스트 추가, 누적 9333 tests
한 줄 본질 정합
Pack 6 형사 도메인 anomaly closed-loop completion — 시그널 감지 → LLM 가설 → action library 보강 → 운영자 즉시 행동.
본 세션 2 capability 가 한 줄 본질의 closed-loop 마감:
- action layer 완성 — 시그널 → 가설 → 액션 4 source × 2 status 매트릭스 cover. anomalyHypotheses 카드가 표시만 하는 게 아니라 행동 가능
- naming 정합 — Pack 1~6 도메인 통합 후 5 sweep (5 + 6 + 7 + 8 + 9) 가 모두 같은 SSoT (CASE_DOMAIN_TYPES) 로 수렴
누적 (2026-05-15~18 전체)
- 머지 PR 92 + 1 = 93건 (squash, 본 PR 1건 포함 예정)
- 9333 누적 테스트 통과 (web 8493 + ops 845)
- 한 줄 본질 핵심 기능 누적:
- #17~#32 (선행 16 세션)
- #33: anomaly action library + CaseDomainType canonical swap ← Pack 6 운영 anomaly 행동 layer + naming 정합
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 9 단계 평균 처리 일수 ETA 산출 — 사무소 별 historical 데이터 baseline 후 추가
- apps/web 잔여 RecoveryType import 사이트 점진 sweep —
@deprecatedJSDoc 가 가르쳐주는 위치 1-2 파일/세션 (e.g. types.ts, criminal-portfolio.ts, party-labels.ts) - anomaly 가설 카드 UI 보강 — curated action 의 scenarioCardId 가 ops scenarios 페이지로 deep link
- 외부 환경 의존 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-18 후속 #34 — CaseDomainType sweep 10 (10 파일 일괄) (1 capability)
자율 모드 — 후속 #33 의 다음 세션 후보 5건 중 자율 가능 1건 (#3 sweep 10) ship. 외부 환경 의존 4건 잔여.
머지 PR (1 commit, branch feat/case-domain-type-sweep-10)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| ADR 0028 sweep 10 | #1 RECOVERY_TYPE_LABEL → CASE_DOMAIN_TYPE_LABEL | 10 파일 일괄 perl 정규식 sweep (negative lookbehind (?<![A-Za-z])). 5 adapter (answer-brief · complaint · demand-notice · payment-order · preparatory-brief) + cases/_lib (case-recovery-type · case-summary-print · recovery-snapshot-schema) + legacy/_lib (chronicle-chapters · v2-query-expansion). recovery-snapshot-schema 가 RECOVERY_TYPES re-export 도 CASE_DOMAIN_TYPES 로 swap (consumer 0건 확인 후 안전 진행) |
Ship 한 capability
- deprecated alias 사용 사이트 10건 정리 — @deprecated JSDoc 도입 후 첫 점진 sweep. 잔여 약 ~10 파일 (UI 컴포넌트 + types.ts + dashboard 카드). 모든 변경 alias referentially 동일, 동작 무변화
기술 결정
| 결정 | 이유 |
|---|---|
1 PR 1 패턴 (RECOVERY_TYPE_LABEL) | RecoveryType 타입은 cases 컴포넌트 50+ 곳 호출, 한 PR 으로 묶으면 diff 폭주. label const 만 먼저 청소 |
| perl 정규식 + negative lookbehind | RecoveryTypeLabel 같은 흡사 토큰 보존. 이전 sweep 7 사고 (sed BSD 한계) 회피 |
| recovery-snapshot-schema re-export rename 가능 확인 후 진행 | consumer 0 확인 — from "...recovery-snapshot-schema" import 중 RECOVERY_TYPES 가져가는 곳 없음. break risk 0 |
검증
- apps/web type-check 통과
- apps/web 709 test files / 8488 tests 통과 (parity 가드 + 모든 cases adapter 테스트 그대로 통과)
한 줄 본질 정합
Pack 1~6 19 종 도메인의 IDE 가르침 효과 production — 신규 코드 자연스럽게 CaseDomainType 사용.
본 세션 1 capability 가 코드베이스 일관성 보강:
- 점진 sweep 시동 — sweep 9 의 @deprecated 가 IDE 에서 가르치는 위치 중 10건 청소. 다음 세션이 잔여 ~10건 마저 정리하면 sweep 완료
누적 (2026-05-15~18 전체)
- 머지 PR 93 + 1 = 94건 (squash, 본 PR 1건 포함 예정)
- 8488 누적 테스트 통과 (web 8488, ops 845 변동 없음)
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 9 단계 평균 처리 일수 ETA 산출 — 사무소 별 historical 데이터 baseline 후 추가
- sweep 11 — UI 컴포넌트 (RecoveryTypeControls · RecoveryTab · RecoveryTypeChangeAlert · ClientInsightCard) + dashboard 카드 (DashboardRecoveryTypeCard) + wizard config (~10 파일 잔여)
- anomaly 가설 카드 UI 보강 — curated action 의 scenarioCardId 가 ops scenarios 페이지로 deep link
- 외부 환경 의존 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-18 후속 #35 — Pack 6.23 anomaly 카드 시나리오 deep link (1 capability)
자율 모드 — 후속 #34 의 다음 세션 후보 5건 중 자율 가능 1건 (#4 anomaly card scenarioCardId deep link) ship. 외부 환경 의존 4건 잔여.
머지 PR (1 commit, branch feat/anomaly-card-scenario-deeplink)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.23 anomaly deep link | #1 추천 액션 → ops 시나리오 1 클릭 진입 | apps/ops/app/(ops)/dashboard/_lib/scenario-card-link.ts (NEW) — resolveScenarioCardPath(id) 순수 함수가 SCENARIO_CATEGORIES 순회로 cardId → /scenarios/[domain]/[card] 경로 변환. 미등록 → null. DynamicHypothesisCard 가 scenarioCardId resolves 시 Next.js Link 로 렌더 (blue underline). suggested-actions.ts 의 curated scenarioCardId 도 정정 — 실제 등록 카드 (record-rag-eval-run·record-refinement-feedback) 만 deep link, 운영 가이드형 (criminalStuck·docgenStats) 은 plain text |
Ship 한 capability
- 추천 액션 1 클릭 진입 — anomaly 가설 카드 추천 액션 중 ops 시나리오 카드 매핑된 액션은 클릭만으로 시나리오 페이지 진입. 운영자가 추천 받자마자 즉시 시나리오 실행으로 closed-loop
- fallback safety — 미등록 scenarioCardId 면 plain text 로 fallback, broken link 0 보증 (정규식 sweep 시 cardId 가 외래 키처럼 동작)
기술 결정
| 결정 | 이유 |
|---|---|
| 순수 함수 lookup (SCENARIO_CATEGORIES 순회) | O(N) 이지만 N ≈ 90 카드로 작음. Map cache 도입 시 SCENARIO_CATEGORIES 변경 동기화 부담 — 매 호출 순회가 충분히 빠름. 18 테스트로 회귀 차단 |
dashboard _lib/ 에 위치 (공유 lib 아님) | 현재 단일 호출 사이트. 다른 페이지에서 쓰일 때 apps/ops/lib/ 로 promote 검토 |
| curated 액션의 scenarioCardId 자정 (재정의 아닌 정정) | 처음 임의로 만든 ID 가 실제 SCENARIO_CATEGORIES 에 없었음. 직접 매핑 가능한 카드만 ID 보존 — operational guide 형 액션은 plain text (web URL 직접 표기) |
| Next.js Link 사용 | client-side navigation, prefetch 자동. <a href> 사용 시 풀 리로드 비용 |
검증
- apps/ops type-check + 108 test files (851 tests) 통과 (+6 신규)
- apps/web type-check + 709 test files (8488 tests) 통과 (suggested-actions 12 testes 갱신)
한 줄 본질 정합
anomaly closed-loop 의 마지막 step — 가설·시그널 → 액션 → 시나리오 진입 모두 한 카드에서.
본 세션 1 capability 가 운영자 안목 + 행동 사이의 마찰 제거:
- deep link — anomaly 가설 카드 자체가 시나리오 진입점. ops 콘솔 navigation 0
누적 (2026-05-15~18 전체)
- 머지 PR 94 + 1 = 95건 (squash, 본 PR 1건 포함 예정)
- 9339 누적 테스트 통과 (web 8488 + ops 851)
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 9 단계 평균 처리 일수 ETA 산출
- sweep 11 — UI 컴포넌트 (RecoveryTypeControls · RecoveryTab · RecoveryTypeChangeAlert · ClientInsightCard) + dashboard 카드 (DashboardRecoveryTypeCard) + wizard config (~10 파일 잔여)
- anomaly 가설 카드 추가 정합 — proposedActions 의 web URL (
/cases?caseFlowStage=5) 도 진짜 Link 로 렌더 (현재는 label 텍스트 안에 string) - 외부 환경 의존 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-18 후속 #36 — CaseDomainType sweep 11 (12 파일) (1 capability)
자율 모드 — 후속 #35 의 다음 세션 후보 5건 중 자율 가능 1건 (#3 sweep 11) ship.
머지 PR (1 commit, branch feat/case-domain-type-sweep-11)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| ADR 0028 sweep 11 | #1 UI/dashboard/wizard alias rename | 12 파일 perl 정규식 sweep — _recovery 3 (RecoveryResultSummary · RecoverySnapshotHistory · RecoveryTypeChangeAlert) + RecoveryTab + cases/new 3 (CaseWizardSuccess · Step3DomainInfo · wizardConfig) + ClientInsightCard + DashboardRecoveryTypeCard + docgen-adapter + legacy ScannedUploadModal · case-type-normalize. wizardConfig 만 RecoveryType 타입 import → CaseDomainType (4 occurrence). 나머지 11 파일은 RECOVERY_TYPE_LABEL → CASE_DOMAIN_TYPE_LABEL |
Ship 한 capability
- production code alias cleanup 진척 — sweep 10 (10 파일) + sweep 11 (12 파일) = 22 파일 production alias 정리. 잔여 약 ~5 파일 (test 파일은 별도 sweep 예정)
기술 결정
| 결정 | 이유 |
|---|---|
| 1 PR 12 파일 | sweep 10 (10 파일) 검증 후 1.2배 규모 진행. perl 정규식 + type-check + 전체 test 1회 통과로 회귀 0 |
| test 파일 별도 sweep 보류 | 테스트는 직접 영향 없음. 한 번에 모두 정리할 때까지 알리아스 동작 보존 — 같은 PR 에 묶으면 diff 폭주 |
| 다음 세션 candidate 에 anomaly Link 추가 정합 등록 | sweep 마감까지 1-2 PR 남음. anomaly 카드의 web URL 진짜 Link 화 가능성 candidate 보존 |
검증
- apps/web type-check + 709 test files (8488 tests) 통과
한 줄 본질 정합
코드베이스 명명 일관성 — 다음 sweep 1-2 회 후 마감.
누적 (2026-05-15~18 전체)
- 머지 PR 95 + 1 = 96건 (squash, 본 PR 1건 포함 예정)
- 9339 누적 테스트 통과
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 9 단계 평균 처리 일수 ETA 산출
- sweep 12 — 잔여 test 파일 ~13 (non-debt-parity-* + business-logic-* + recovery-type-icon-map.test + RecoveryResultSummary.logic.test + party-labels.test) 일괄
- anomaly 가설 카드 web URL 진짜 Link 화 —
/cases?caseFlowStage=5텍스트 매칭 → Next.js Link - 외부 환경 의존 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-18 후속 #37 — CaseDomainType sweep 12 (test 파일 19) (1 capability)
자율 모드 — 후속 #36 의 다음 세션 후보 5건 중 자율 가능 1건 (#3 sweep 12) ship.
머지 PR (1 commit, branch feat/case-domain-type-sweep-12)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| ADR 0028 sweep 12 | #1 test 파일 alias rename | 19 test 파일 perl 정규식 sweep — non-debt-parity-A1~A14 (14) + business-logic-cases-firm/client-doc-checklist + RecoveryResultSummary.logic.test + recovery-type-icon-map.test + party-labels.test. 3 패턴 (RECOVERY_TYPES · RECOVERY_TYPE_LABEL · RECOVERY_TYPE_VALUES). 명시적 alias 계약 테스트 (recovery-input-schemas.test · recovery-type-constants.test) 는 sweep 제외 — 양쪽 alias 동작 검증 가드로 유지 |
Ship 한 capability
- alias sweep 누적 41 파일 마감 (sweep 10 10 + sweep 11 12 + sweep 12 19). 잔여 alias 사용 사이트 = 2 (의도적 alias 계약 테스트)
기술 결정
| 결정 | 이유 |
|---|---|
| recovery-input-schemas.test / recovery-type-constants.test 보존 | 두 테스트는 alias 동작 회귀 가드 — RecoveryTypeSchema.safeParse(...)·Object.keys(RECOVERY_TYPE_LABEL) 등 alias 가 실제로 working 임을 검증. 신규 코드는 안 쓰지만 alias 보존 contract 강제 |
| 19 파일 1 PR | sweep 11 12 파일 후 검증 패턴 충분히 성숙. type-check + 8488 tests 1회 통과로 회귀 0 |
| RecoveryType 타입 자체는 별도 sweep | Step1RecoveryType.tsx (filename) · recoveryType (Firestore field name) 같은 식별자 충돌 위험. 이후 컴포넌트 rename 시 함께 |
검증
- apps/web type-check 통과
- apps/web 709 test files / 8488 tests 통과
한 줄 본질 정합
alias migration 마감 step — 41 파일 정리 후 잔여 = alias 가드 2 테스트만.
누적 (2026-05-15~18 전체)
- 머지 PR 96 + 1 = 97건 (squash, 본 PR 1건 포함 예정)
- 9339 누적 테스트 통과
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 9 단계 평균 처리 일수 ETA 산출
- component-level RecoveryType identifier rename — Step1RecoveryType.tsx, RecoveryTab.tsx, RecoveryTypeChangeAlert.tsx, RecoveryTypeControls.tsx, RecoveryTypeSelector.tsx 등 파일·컴포넌트 rename (Pack 6.X sub-numbered, 후속 작업)
- anomaly 가설 카드 web URL 진짜 Link 화 — 우선순위 낮음 (cross-app 복잡도 + 단일 occurrence)
- 외부 환경 의존 4건 (dev hydration · production Gemini · 시각 검수 · e2e)
2026-05-18 후속 #38 — Pack 6.21 형사 9 단계 평균 정체 일수 (1 capability)
자율 모드 — 후속 #37 의 다음 세션 후보 5건 중 자율 가능 1건 (#2 ETA) ship.
머지 PR (1 commit, branch feat/pack6-21-criminal-phase-eta)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.21 avgDaysByPhase | #1 단계별 평균 정체 일수 | CriminalPortfolioStats.avgDaysByPhase: Partial<Record<CriminalPhase, number>> 추가. computeCriminalPortfolio 가 활성 사건 (finalized/enforced 제외) 의 daysAtCurrentPhase 를 단계 별로 모아 mean 계산. DashboardCriminalPortfolioCard 가 9 단계 분포 row 에 "평균 N일" inline 표시 (Math.round). 5 신규 단위 테스트 |
Ship 한 capability
- 변호인 단계별 정체 가시화 — dashboard 의 9 단계 분포가 단순 건수에서 "단계 X 에서 평균 Y 일 정체" 까지 보여줌. stuckCount (90일+) 와 보완 관계 — stuckCount 는 극단 표본, avgDays 는 모든 활성 사건 평균
- historical baseline 없이도 운영 가시화 가능 — 진정한 단계 간 전이 평균 일수 (ETA) 는 phase transition log 필요. 본 PR 은 활성 sampling proxy 로 동등한 운영 통찰 제공
기술 결정
| 결정 | 이유 |
|---|---|
| 활성 sampling (현재 단계 머문 일수) | phase transition log infrastructure 없이 즉시 운영 가능. 진정한 ETA 는 historical 데이터 누적 후 별도 sweep. 변호인 의사결정에 active sampling 충분 (어느 단계 처리 부담 큰지 즉시 보임) |
Partial<Record<...>> (optional keys) | sample 0 단계는 키 없음 → UI 가 자연스럽게 fallback ("평균 N일" 행 미표시) |
Math.round 정수화 | 운영 표시 "45일" vs "44.7일" 둘 다 같은 의사결정 결과. 정수가 가독성 우선 |
| inline "· 평균 N일" 표시 | 별도 라인 추가 시 카드 vertical density 폭증. count/% 와 한 줄에 — text-[10px] 작게 |
검증
- apps/web type-check 통과
- apps/web 709 test files / 8493 tests 통과 (+5 신규)
한 줄 본질 정합
형사 portfolio 단계별 처리 속도 시각화 완성 — 변호인이 dashboard 5초 헤드라인에서 "어디서 정체" 직관.
누적 (2026-05-15~18 전체)
- 머지 PR 97 + 1 = 98건 (squash, 본 PR 1건 포함 예정)
- 9344 누적 테스트 통과
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 단계 간 전이 평균 일수 (진정한 ETA) — phase transition activity log 누적 후
- component-level RecoveryType identifier rename (계속)
- anomaly 가설 카드 web URL 진짜 Link 화 (우선순위 낮음)
- 외부 환경 의존 4건
2026-05-18 후속 #39 — Pack 6.24 anomaly 카드 web URL cross-origin 앵커 (1 capability)
자율 모드 — 후속 #38 의 다음 세션 후보 5건 중 자율 가능 1건 (#4 anomaly Link) ship.
머지 PR (1 commit, branch feat/anomaly-card-web-url-link)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| Pack 6.24 anomaly URL anchor | #1 label 안 path 자동 앵커 | apps/ops/lib/web-app-url.ts — getWebAppOrigin() (dev/test localhost:3000, prod NEXT_PUBLIC_WEB_APP_ORIGIN env 우선 + same-domain fallback) + toWebAppUrl(path) + WEB_APP_PATH_REGEX (9 root path 보수적). apps/ops/app/(ops)/dashboard/_lib/render-action-label.tsx — label text 에서 web 앱 path 발견 시 ReactNode 로 split + <a target="_blank"> 앵커. DynamicHypothesisCard 가 scenarioCardId 없으면 renderActionLabel 호출 (있으면 기존 Link). 9 신규 단위 테스트 |
Ship 한 capability
- anomaly 카드 액션 1 클릭 진입 완전화 — scenarioCardId 등록 카드는 Next.js Link (Pack 6.23), label 안 web path 는 cross-origin 앵커 (Pack 6.24). 운영자가 가설 카드에서 모든 추천 액션을 1 클릭 진입
- 9 root path 보수적 매칭 —
/cases·/dashboard·/legacy·/portal·/docs·/clients·/finance·/revenue·/settings. 자유 텍스트 안의 우연한/word토큰은 매칭 안 함
기술 결정
| 결정 | 이유 |
|---|---|
Next.js Link 대신 plain <a target="_blank"> | cross-origin (ops → web) navigation. Next.js Link 는 same-origin 가정 → 의도와 다른 ops origin 으로 잘못 라우팅 |
getWebAppOrigin() 3 단계 fallback | dev localhost:3000 (JourneyRunPanel 동일) → prod env → window.location.origin (same-domain 배포). server-side render 부재 → 빈 문자열 (의도 — env 설정 강제) |
regex 안 \?[A-Za-z0-9_=&%.-]* query string | encoded char 일부 (% .) 허용. 한국어 query 미지원 — caller 가 ASCII encoded path 전달 |
| 9 root 화이트리스트 (탐색 회피) | "/cases" 등 root path 만 매칭. 무차별 /[a-z]+ 패턴 사용 시 /cases?caseFlowStage=5 진행 중 의 "진행" 같은 단어가 path 시작으로 오인 가능 |
검증
- apps/ops type-check + 109 test files (860 tests, +9 신규) 통과
- 9353 누적 (web 8493 + ops 860)
한 줄 본질 정합
anomaly closed-loop UI 정합화 — 가설 + scenarioCardId Link + label web URL 앵커 3 layer.
누적 (2026-05-15~18 전체)
- 머지 PR 98 + 1 = 99건 (squash, 본 PR 1건 포함 예정)
- 9353 누적 테스트 통과
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- 단계 간 전이 평균 일수 (진정한 ETA) — phase transition activity log 누적 후
- component-level RecoveryType identifier rename (계속)
- NEXT_PUBLIC_WEB_APP_ORIGIN env 정합 (env.example 추가 · production 배포 절차)
- 외부 환경 의존 4건
2026-05-18 후속 #40 — env 정합 + sweep 13 (RecoveryType type identifier 3 파일) (2 capability)
자율 모드 — 후속 #39 의 다음 세션 후보 5건 중 자율 가능 2건 (#3 sweep + #4 env) 일괄 ship.
머지 PR (2 commit, branch feat/web-app-origin-env)
| commit | 분류 | 핵심 변경 |
|---|---|---|
| docs(env) NEXT_PUBLIC_WEB_APP_ORIGIN | #1 env example 정합 | apps/ops/.env.example 에 NEXT_PUBLIC_WEB_APP_ORIGIN 등록. dev/test 미설정 = localhost:3000, prod 미설정 = same-domain fallback. Pack 6.24 anomaly URL 앵커 + JourneyRunPanel cross-app 진입 패턴 표준화 |
| ADR 0028 sweep 13 | #2 RecoveryType type identifier 3 파일 | step3-schema.ts + party-labels.ts + Step3DomainInfo.tsx 의 type RecoveryType import + 함수 시그너처 매개변수 RecoveryType 식별자 → CaseDomainType. negative lookbehind 로 PreviewRecoveryType 같은 다른 식별자 보존 |
Ship 한 capability
- env 명세 완비 — Pack 6.24 의 cross-origin URL 앵커 옵션이 .env.example 에 명시. production 다른 도메인 배포 시 1 줄 설정으로 활성화
- type identifier sweep 누적 44 파일 (sweep 10+11+12+13 production 25 + test 19). 잔여 = 컴포넌트 file rename (Step1RecoveryType.tsx) + local 식별자 + 별도 의미의 corpus enum (case-classification-corpus)
기술 결정
| 결정 | 이유 |
|---|---|
| env example 만 — 코드 추가 변경 0 | Pack 6.24 의 getWebAppOrigin() 이 이미 env 분기 처리. 본 PR 은 명세 정합 (개발자 onboarding) |
| 컴포넌트 file rename (Step1RecoveryType.tsx) 보류 | 8+ 파일 string 참조 (test grep 패턴, e2e specs 등) 수정 필요. 미충분한 가치 (aesthetic) 대비 break risk 큼. local 식별자 Step1RecoveryType (AiPrefillCard 등) 도 internal naming 으로 유지 |
| case-classification-corpus 의 RecoveryType 별도 enum 유지 | AI 분류 corpus 의 local 도메인 enum — 시맨틱 분리. canonical CaseDomainType 와 별개 결합도 0 |
검증
- apps/web type-check 통과
- apps/web 709 test files / 8493 tests 통과
한 줄 본질 정합
운영 가이드 + 명명 일관성 — production deployment 정합 + 식별자 sweep 누적 44 파일.
누적 (2026-05-15~18 전체)
- 머지 PR 99 + 1 = 100건 (squash, 본 PR 1건 포함 예정) 🎉
- 9353 누적 테스트 통과
다음 세션 후보
- CriminalDefaultsSection e2e (외부 환경)
- phase transition activity log (진정한 ETA baseline) — Pack 6.21 후속
- Step1RecoveryType.tsx 파일 rename → Step1CaseDomainType.tsx (8+ 파일 단일 PR — 별도 세션)
- AnomalyHypothesis schema 확장 — proposedActions 의
hreffield (label 의존 회피, 명시 URL/scenarioCardId 분리) - 외부 환경 의존 4건