release: 2026-04-20 (37건 커밋) #89

병합
htlee release/2026-04-20 에서 main 로 23 commits 를 머지했습니다 2026-04-20 06:56:08 +09:00

23 커밋

작성자 SHA1 메시지 날짜
be315f59aa docs: 릴리즈 노트 정리 (2026-04-20) 2026-04-20 06:52:25 +09:00
30abe6a951 Merge pull request 'feat(db): Detection Model Registry 스키마 (V034, Phase 1-1)' (#87) from feature/phase1-1-detection-models-schema into develop 2026-04-20 06:49:02 +09:00
82ffe7cb06 Merge branch 'develop' into feature/phase1-1-detection-models-schema 2026-04-20 06:48:43 +09:00
afde36480d Merge pull request 'fix(ui): 모니터링/디자인시스템 런타임 에러 해소' (#88) from fix/runtime-errors-monitoring-designsystem into develop 2026-04-20 06:36:32 +09:00
d971624090 docs: 릴리즈 노트 업데이트 2026-04-20 06:31:33 +09:00
eee9e79818 fix(ui): 모니터링/디자인시스템 런타임 에러 해소
### SystemStatusPanel TypeError
- 증상: /monitoring 에서 Uncaught TypeError: Cannot read properties of undefined (reading 'toLocaleString')
- 원인: stats 객체는 존재하나 total 필드가 undefined 인 경우 (백엔드 응답이 기대 shape 와 다를 때) 크래시
- 수정: stats?.total != null ? ... / stats.critical ?? 0 식 null-safe 전환 (total/clusterCount/gearGroups/critical/high/medium/low 전부)

### CatalogBadges 렌더링 오류
- 증상: /design-system.html 에서
  (1) Each child in a list should have a unique "key" prop
  (2) Objects are not valid as a React child (found: object with keys {ko, en})
- 원인: PERFORMANCE_STATUS_META 의 meta 는 {intent, hex, label: {ko, en}} 형식. code 필드 없고 label 이 객체.
  - Object.values() + <Trk key={meta.code}> 로 undefined key 중복
  - getKoLabel 이 meta.label (객체) 그대로 반환해 Badge children 에 객체 주입
  다른 카탈로그는 fallback: {ko, en} 패턴이라 문제 없음 (performanceStatus 만 label 객체)
- 수정:
  - Object.entries() 로 순회해 Record key 를 안정적 식별자로 사용
  - AnyMeta.label 타입을 string | {ko,en} 확장
  - getKoLabel/getEnLabel 우선순위: fallback.ko → label.ko → label(문자열) → code → key
  - PERFORMANCE_STATUS_META 자체는 변경 안 함 (admin 페이지들이 label.ko/label.en 직접 참조 중)

### 검증
- npx tsc --noEmit 통과
- pre-commit tsc+ESLint 통과
2026-04-20 06:31:11 +09:00
410d0da9cf docs: 릴리즈 노트 업데이트 2026-04-20 06:22:58 +09:00
dae7aea861 feat(db): Detection Model Registry 스키마 (V034, Phase 1-1)
prediction 의 17 탐지 알고리즘을 "명시적 모델 단위" 로 분리하고 프론트엔드에서
파라미터·버전을 관리할 수 있도록 하는 기반 인프라의 DB 계층.

기존 V014 correlation_param_models 패턴을 일반화:
- JSONB params + is_active(V014) → model_id × version × role×status (V034)
- 한 모델을 여러 파라미터 셋으로 동시 실행 지원 (PRIMARY/SHADOW/CHALLENGER)
- Compare API 기반 제공 (PRIMARY vs SHADOW diff 집계)

### 스키마 (테이블 4 + 뷰 1)

1. detection_models — 모델 카탈로그 (model_id PK, tier 1~5, category, entry_module/callable, is_enabled)
2. detection_model_dependencies — 모델 간 DAG 엣지 (self-loop 금지, input_key 포함)
3. detection_model_versions — 파라미터 스냅샷 + 라이프사이클 + role
   - status: DRAFT / TESTING / ACTIVE / ARCHIVED
   - role: PRIMARY / SHADOW / CHALLENGER (ACTIVE 일 때만)
   - UNIQUE partial index: model_id 당 PRIMARY×ACTIVE 최대 1건
   - SHADOW/CHALLENGER×ACTIVE 는 N건 허용 (병렬 비교)
   - parent_version_id 로 fork 계보 추적
4. detection_model_run_outputs — 버전별 실행 결과 원시 snapshot
   - PARTITION BY RANGE (cycle_started_at)
   - 초기 2개월(2026-04, 2026-05) 파티션 + 이후 월별 자동생성 TODO (Phase 1-2)
   - input_ref JSONB GIN index — 같은 입력 기준 PRIMARY×SHADOW JOIN 용
5. detection_model_metrics — 사이클 단위 집계 메트릭 (cycle_duration_ms 등)
6. v_detection_model_comparison — PRIMARY×SHADOW 같은 입력으로 JOIN 한 VIEW

### 권한
- auth_perm_tree: 'ai-operations:detection-models' (parent=admin, nav_sort=250)
- ADMIN 5 ops / OPERATOR READ+UPDATE / ANALYST·VIEWER READ

### 후속 (Phase 1-2)
- partition_manager 에 detection_model_run_outputs 월별 자동 생성/만료 로직 추가
- 기본 retention 7일 (SHADOW 대량 누적 대비)

### 검증
- Flyway 자동 적용 (백엔드 재빌드+배포)
- 이후 Python Model Registry 가 이 스키마 위에서 동작
2026-04-20 06:22:25 +09:00
2395ef1613 Merge pull request 'feat(detection): 환적 의심 전용 탐지 페이지 신설 (Phase 0-3)' (#86) from feature/phase0-3-transshipment-detection into develop 2026-04-20 05:52:05 +09:00
a8ce9a4ea9 docs: 릴리즈 노트 업데이트 2026-04-20 05:51:25 +09:00
cbfed23823 feat(detection): 환적 의심 전용 탐지 페이지 신설 (Phase 0-3)
docs/prediction-analysis.md §7 P1 권고의 "UI 미노출 탐지" 해소 중 두 번째.
prediction algorithms/transshipment.py 5단계 필터 파이프라인 결과를 전체 목록·
집계·상세 수준으로 조회하는 READ 전용 대시보드.

### 배경
기존 features/vessel/TransferDetection.tsx 는 선박 상세 수준(특정 MMSI 의 환적
이력)이고, 환적 의심 선박 전체 목록을 보려면 ChinaFishing 의 탭 중 하나를 거쳐야
했다. /api/analysis/transship 엔드포인트는 이미 존재하나 전용 페이지가 없었음.

### 변경
- frontend/src/features/detection/TransshipmentDetection.tsx 신설 (405 라인)
  - PageContainer + PageHeader(ArrowLeftRight) + KPI 5장
    (Total / Transship tier CRITICAL/HIGH/MEDIUM / Risk CRITICAL)
  - DataTable 8컬럼 (analyzedAt / mmsi / pairMmsi / duration / tier / risk / zone)
  - features.transship_tier 읽어 Badge 로 심각도 표시
  - 필터: hours(1/6/12/24/48) / riskLevel / mmsi 검색
  - 상세 패널: 분석 피처 JSON 원본 + 좌표 + transship_score
  - 기존 analysisApi.getTransshipSuspects 재사용 — backend 변경 없음
- index.ts + componentRegistry.ts 등록
- detection.json (ko/en) transshipment.* 네임스페이스 추가 (각 44키)
- common.json (ko/en) nav.transshipment 추가
- V033__menu_transshipment_detection.sql
  - auth_perm_tree(detection:transshipment, nav_sort=910)
  - ADMIN 5 ops + OPERATOR/ANALYST/FIELD/VIEWER READ

### 권한 주의
/api/analysis/transship 의 @RequirePermission 은 현재 detection:dark-vessel.
이 메뉴 READ 만으로는 API 호출 불가. 현행 운영자 역할(OPERATOR/ANALYST/FIELD)
은 dark-vessel READ 도 보유하므로 실용 동작.
향후 VesselAnalysisController.listTransshipSuspects 의 @RequirePermission 을
detection:transshipment 로 교체하는 권한 일관화는 별도 MR (후속).

### 검증
- npx tsc --noEmit 통과
- pre-commit tsc + ESLint 통과 예정
- Flyway V033 자동 적용 (백엔드 재배포 필요)
2026-04-20 05:51:06 +09:00
f2d145c9a2 Merge pull request 'feat(detection): 불법 조업 이벤트 전용 페이지 신설 (Phase 0-2)' (#85) from feature/phase0-2-illegal-fishing-pattern into develop 2026-04-20 05:47:17 +09:00
214f063f1e docs: 릴리즈 노트 업데이트 2026-04-20 05:46:34 +09:00
e49ab0f4e8 feat(detection): 불법 조업 이벤트 전용 페이지 신설 (Phase 0-2)
docs/prediction-analysis.md §7 P1 권고의 "UI 미노출 탐지" 해소. event_generator
가 생산하는 카테고리 중 불법 조업 관련 3종을 READ 전용 대시보드로 통합.

대상 카테고리:
- GEAR_ILLEGAL   — G-01 수역·어구 / G-05 고정어구 drift / G-06 쌍끌이
- EEZ_INTRUSION  — 영해 침범 / 접속수역 + 고위험
- ZONE_DEPARTURE — 특정수역 진입 (risk ≥ 40)

### 변경
- frontend/src/services/illegalFishingPatternApi.ts 신설
  - 기존 /api/events 를 category 다중 병렬 조회 후 머지 (backend 변경 없음)
  - category '' 이면 3 카테고리 통합, 지정 시 단일 카테고리만
  - size 기본 200 × 3 categories = 최대 600건, occurredAt desc 정렬
  - byCategory / byLevel 집계 포함
- frontend/src/features/detection/IllegalFishingPattern.tsx 신설 (391 라인)
  - PageContainer + PageHeader(Ban 아이콘) + Section + KPI 5장 + 카테고리별 3장
  - DataTable (occurredAt/level/category/title/mmsi/zone/status 7컬럼)
  - 필터: category / level / mmsi (최근 DEFAULT_SIZE 건 범위)
  - 상세 패널: JSON features 포함, EventList 로 네비게이션 링크
  - design-system SSOT 준수: Badge intent, Select aria-label, text-* 시맨틱 토큰
- index.ts + componentRegistry.ts export/lazy 등록
- detection.json (ko/en) illegalPattern.* 네임스페이스 추가 (각 60키)
- common.json (ko/en) nav.illegalFishing 추가
- V032__menu_illegal_fishing_pattern.sql
  - auth_perm_tree 엔트리 (rsrc_cd=detection:illegal-fishing, nav_sort=920)
  - ADMIN 5 ops + OPERATOR/ANALYST/FIELD/VIEWER READ
  - READ 전용 페이지 (처리 액션은 EventList 경유)

### 검증
- npx tsc --noEmit 통과 (0 에러)
- 백엔드 변경 없음 (기존 /api/events category 필터 재사용)
- Flyway V032 자동 적용 (백엔드 재배포 필요)
2026-04-20 05:46:13 +09:00
2e674ccc5b Merge pull request 'fix(db): candidate_source 컬럼 VARCHAR(30)→(100) 확장 (V031)' (#84) from fix/candidate-source-length into develop 2026-04-17 11:53:08 +09:00
2b25dc1c92 docs: 릴리즈 노트 업데이트 2026-04-17 11:52:08 +09:00
0f4a9cb7d6 fix(db): candidate_source 컬럼 VARCHAR(30)→(100) 확장 (V031)
gear_group_parent_candidate_snapshots.candidate_source 의 VARCHAR(30) 제약
때문에 prediction gear_correlation 스테이지가 매 사이클 실패하던 문제 해소.

원인:
- prediction/algorithms/gear_parent_inference.py:875 의
  candidate_source = ','.join(sorted(meta['sources']))
  가 복수 source 라벨 (CORRELATION/EPISODE/LABEL/LINEAGE/MATCH) 을 쉼표 join
  하며 최대 약 39자. VARCHAR(30) 초과 시 psycopg2.errors.StringDataRightTruncation
  을 유발해 _insert_candidate_snapshots 전체 ROLLBACK.

발견 경위:
- Phase 0-1 (PR #83) 의 stage_runner + logger.exception 전환 후 journal 에
  찍힌 풀 스택트레이스로 드러남. 기존에는 logger.warning 한 줄 ("gear
  correlation failed: ...") 만 남아 원인 특정 불가.

영향 범위:
- 백엔드 JPA 엔티티 미참조 → 재빌드·재배포 불필요
- Flyway 자동 적용 (백엔드 기동 시)
- prediction 재기동만 필요 (기존 코드 그대로, 이제 INSERT 성공 기대)

검증:
- 재배포 후 journalctl 에서 'gear correlation failed' 로그 사라짐 확인
- kcg.gear_group_parent_candidate_snapshots 에 최근 15분 건수 증가 확인
2026-04-17 11:51:35 +09:00
3e29bc9995 Merge pull request 'refactor(prediction): 사이클 스테이지 에러 경계 도입 (Phase 0-1)' (#83) from feature/phase0-cycle-error-boundary into develop 2026-04-17 11:30:17 +09:00
a32d09f75a docs: 릴리즈 노트 업데이트 2026-04-17 11:28:58 +09:00
197da13826 refactor(prediction): 사이클 스테이지 에러 경계 도입 (Phase 0-1)
docs/prediction-analysis.md P1 권고 반영. 5분 사이클의 각 스테이지를
한 try/except 로 뭉친 기존 구조를 스테이지 단위로 분리해 실패 지점을
명시적으로 특정하고 부분 실패 시에도 후속 스테이지가 계속 돌아가도록 개선.

- prediction/pipeline/stage_runner.py 신설
  - run_stage(name, fn, *args, required=False, **kwargs) 유틸
  - required=True 면 예외 re-raise (상위 사이클 try/except 가 잡도록)
  - required=False 면 logger.exception 으로 stacktrace 보존 + None 반환
  - 지속시간 로깅 포함

- prediction/scheduler.py run_analysis_cycle() 수정
  - 출력 단계 6모듈을 각각 run_stage() 로 분리:
    violation_classifier / event_generator / kpi_writer /
    stats_aggregate_hourly / stats_aggregate_daily / alert_dispatcher
  - upsert_results / cleanup_old 도 run_stage 로 래핑 (upsert 는 required=True)
  - 내부 try/except 의 logger.warning → logger.exception 으로 업그레이드
    (fetch_dark_history, gear collision event promotion, group polygon,
     gear correlation, pair detection, chat cache)
  - 스테이지 실패 시 journalctl -u kcg-ai-prediction 에서 stacktrace 로
    원인 바로 특정 가능 (기존은 "failed: X" 한 줄만 남아 디버깅 불가)

검증:
- python3 -c "import ast; ast.parse(...)" scheduler.py / stage_runner.py 통과
- run_stage smoke test (정상/실패 흡수/required 재raise 3가지) 통과

범위 밖 (후속):
- Phase 0-2 ILLEGAL_FISHING_PATTERN 전용 페이지 (다음 MR)
- Phase 0-3 Transshipment 전용 페이지 (다음 MR)
2026-04-17 11:28:30 +09:00
bae2dbde08 Merge pull request 'docs: prediction-analysis 신규 + 루트/SFR 문서 drift 해소' (#82) from feature/docs-refresh-2026-04-17 into develop 2026-04-17 11:22:41 +09:00
451f38036a docs: 릴리즈 노트 업데이트 2026-04-17 11:21:56 +09:00
b37e18d952 docs: prediction-analysis 신규 + 루트/SFR 문서 drift 해소
- docs/prediction-analysis.md 신설 — opus 4.7 독립 리뷰 기반 prediction 구조/방향 심층 분석
  (9개 섹션: 아키텍처·5분 사이클·17 알고리즘·4대 도메인 커버리지·6축 구조 평가·개선 제안 P1~P4·임계값 전수표)
- AGENTS.md / README.md — V001~V016→V030, Python 3.9→3.11+, 14→17 알고리즘 모듈
- docs/architecture.md — /gear-collision 라우트 추가 (26→27 보호 경로)
- docs/sfr-traceability.md — V029→V030, 48→51 테이블, SFR-10 에 GEAR_IDENTITY_COLLISION 추가
- docs/sfr-user-guide.md — 어구 정체성 충돌 페이지 섹션 신설
- docs/system-flow-guide.md — 노드 수 102→115, V030 manifest 미반영 경고
- backend/README.md — "Phase 2 예정" 상태 → 실제 운영 구성 + PR #79 hotfix 요구사항 전면 재작성
2026-04-17 11:20:53 +09:00