# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [2.13.1] — 2026-09-01

### Fixed
- **일일보고에서 시간 단위 연차가 "0시간"으로 표시되던 문제** — 30분 연차(0.0625일)가
  `int(days * 8)` 절삭으로 0시간이 되어 보고서를 받는 담당자가 사용 내역을 알 수 없었다.
  분 단위까지 표시하도록 수정 (`30분`, `1시간 30분`, `1일 4시간`).
- **단축근무 사용자의 연차 시간 환산 오류** — 보고서만 1일=8시간으로 하드코딩되어 있어
  7h30m 근무자의 반차가 4시간으로 표시됐다. DB 정본(`days * work_minutes`)과 동일한
  기준으로 통일 — 이제 3시간 45분.

### Added
- `utils.time_format.format_leave_amount(days, work_minutes)` — 연차 일수를
  (일수, 나머지 분)으로 분해하는 공용 헬퍼 + 자체 검사(`python utils/time_format.py`).
- i18n 키 `report.leave_used_days_time` (`{days}일 {time}` / `{days}d {time}`).

## [2.13.0] — 2026-08-12

### Fixed — 익일(자정 넘김) 퇴근
- **새벽 퇴근이 출근일 기록에 저장되지 않던 버그** — `clock_out()`이 `datetime.now().date()`를
  대상 행으로 써서, 자정을 넘겨 퇴근하면 해당 날짜 행이 없어 `UPDATE`가 0건 처리되고 출근일
  기록이 영구 미퇴근으로 남았다. 근무일 경계(기본 6시) 전까지는 출근일이 근무일이라는
  `handle_workday_rollover()`의 기준을 `clock_out()`에도 적용. `cancel_clock_out()` 동일 수정.
- **야근이 주말/공휴일 근무로 오분류되던 문제** — 근무 성격 판정(`is_non_working_day`/
  `get_day_type`)과 외출 시간 집계를 `now`가 아닌 출근일 기준으로 변경. 금요일 야근이
  토요일 근무로 뒤바뀌어 전 시간이 연장근무로 적립되지 않는다.
- **익일 퇴근 기록을 수정할 수 없던 문제** — 캘린더의 시간 수정과 과거기록 추가 다이얼로그가
  `퇴근 <= 출근`을 입력 오류로 반려해 익일 퇴근을 저장할 방법이 아예 없었다. 이제 익일로
  해석하고, 동시각인 경우만 반려. 총 근무시간·연장근무가 음수로 깨지던 계산도 함께 수정.

### Added
- **일일보고를 다른 날짜로도 복사** — `generate_daily_report(target_date)`로 날짜 파라미터화.
  캘린더에서 날짜 선택 → **보고서 복사** 버튼으로 지난 날짜 보고서를 다시 뽑을 수 있다.
  생략 시 기본값은 진행 중인 근무일(자정~경계시간 사이면 출근일)이라 새벽 퇴근 후에도
  "기록 없음"이 뜨지 않는다.
- **연차 직접입력 버튼** (메인 창) — 연장근무 줄과 동일한 구성. 기존 `AddLeaveDialog`를
  재사용해 날짜(캘린더)/유형/시간/사유를 한 화면에서 입력. 그쪽은 주말·공휴일 차단과
  같은 날 1일 초과 중복 검증까지 갖추고 있다. (구현돼 있었으나 어떤 버튼에도 연결되지
  않았던 `use_custom_leave()`를 연결)
- 익일 퇴근 표기 — 보고서/캘린더 상세에 `익일 01:30`, 수정 다이얼로그에 실시간 안내
  (`🌙 익일 퇴근으로 저장됩니다 (출근~퇴근 16시간 30분)`).
- `core.time_calculator.resolve_work_datetimes()` / `is_overnight()` — 익일 판정을 한 곳으로
  통일 (외출 기록의 자정 처리와 같은 규칙). 유닛 테스트 6개 추가.

### Changed
- **연차 빠른 사용에서 날짜·메모 입력 팝업 제거** — 30분/1시간/반차/종일 버튼이 연장근무
  사용과 동일하게 확인 1회로 끝난다(팝업 3개 → 1개). 날짜·사유 지정은 직접입력으로.
- `Database.get_today_overtime_usage()` / `get_total_break_minutes_today()`에 선택적
  `date_str` 파라미터 추가 (기본값은 기존과 동일한 오늘).

## [2.12.0] — 2026-06-16

### Added — 전체 i18n 키화 완료
- **UI 전체 사용자 대면 문자열 i18n 키화** — `ui/` 디렉터리 22개 파일의 버튼/라벨/메시지박스/차트/온볼딩/설정/통계 등을 `tr()` 기반으로 전환, `core/i18n.py`에 ko/en 번역 추가.
- **도전과제 메타데이터 i18n** — `core/achievements.py`의 모든 도전과제 이름/설명을 `achieve.{code}.name/desc` 키로 분리하고 영문 번역 추가.
- **차트 위젯 라벨 키화** — matplotlib 차트의 축/툴팁/범례/빈 기록 메시지 등을 언어별로 표시.
- **반복 연차 패턴 설명 키화** — `core/recurring_leaves.describe_pattern()`이 요일/주기 접두사를 i18n 키로 조합.

### Fixed
- `ui/help_view.py`의 "온보팅 다시 보기" 버튼이 `tr()` 호출이 아닌 리터럴 문자열로 잘못 들어가던 버그 수정.
- `core/i18n.py` 영문 achievement 번역 중 `Children's`/`Teachers'` 작은따옴표로 인한 SyntaxError 수정.
- `_i18n_gui_test.py`에 `CLOCKOUT_DISABLE_HOLIDAY_SYNC=1` 추가하여 백그라운드 휴일 동기화 스레드로 인한 세그멘테이션 폴트 방지.

### Changed
- **공공데이터포털 공휴일 API 키 외부화** — `utils/holiday_api.py`가 `CLOCKOUT_HOLIDAY_API_KEY` 환경변수를 사용하도록 변경.
- `ui/main_window.py` 1Hz 핫패스 DB 호출 캐싱 추가.
- `utils/csv_importer.py` overwrite 시 `overtime_usage`도 함께 삭제.
- `ui/controllers/lock_monitor.py` 컨텍스트 매니저 적용 및 race condition 처리 개선.

## [2.11.2] — 2026-06-04

### Fixed
- **통계 차트가 빌드(main.exe)에서 안 뜨던 진짜 원인** — frozen 빌드에서 numpy C-확장
  `numpy.core._multiarray_tests`가 누락(`numpy.testing` 제외의 영향)되어 matplotlib import가
  `ModuleNotFoundError`로 실패 → "matplotlib 필요" 폴백. `main.spec`에 해당 모듈 hiddenimport
  추가 + `numpy.testing` 제외 제거. (디버그 로그로 원인 확인: chart_widget이 실패 사유를 기록)
- **도전과제 라이트 테마 가독성** — 헤더 강조 숫자/등급 배지/진행 숫자/진행 바를 라이트에서
  대비 높은 색으로 조정 (다크는 기존 비비드 색 유지).

## [2.11.1] — 2026-06-04

### Fixed
- **빌드(main.exe)에서 통계 차트가 표시되지 않던 문제** — frozen 빌드는 PyInstaller가
  matplotlib `QtAgg`(backend_qtagg)만 번들하는데 `chart_widget`이 `backend_qt5agg`를
  import해 실패 → "matplotlib 필요" 폴백만 보였음. **backend_qtagg 우선 import**(+ qt5agg
  폴백) + 실패 원인 로깅, `main.spec`에 `backend_qtagg`/`PyQt5.sip` 명시.
- **통계·도움말·도전과제 화면이 라이트 테마에서도 다크로 고정되던 문제** — `dark_components`와
  세 화면(+통계 차트 배경/그리드/텍스트)을 현재 테마(`ThemeColors`)에 따르도록 변경.
  다크 기본값은 그대로, 라이트 전환 시 함께 라이트로. 다크 등급 카드/차트 막대 등 강조색은 유지.

## [2.11.0] — 2026-06-04

### Changed — UI 전면 다크 리디자인
- 모던 다크 미니멀 테마(Notion/Linear 톤): 배경 `#1A1B1E` / 카드 `#25262B` / 보더 `#2C2E33`,
  단일 포인트 컬러 `#4DABF7`(주요 버튼·포커스 전용), 텍스트 `#E9ECEF`/`#909296`
- **다크가 기본 테마** (신규 설치 기준; 기존 사용자가 고른 설정은 보존)
- 번들 폰트 **NanumSquare** (`font/`, `utils/font_loader.py`) — OS 미설치 시 Malgun Gothic 폴백,
  `main.spec`에 동봉
- 통일 여백(외곽 24 / 위젯 12 / 카드 16), border-radius 8px, 버튼 그라데이션·베벨 제거(flat),
  입력 포커스 시 보더 컬러만 accent, 진행률 바 6px
- 남은시간 히어로 영역(출근/현재 한 줄 + 예상 퇴근시각 통합), 퇴근 가능 시 그린(`#51CF66`) 피드백

### Added
- **라인 아이콘 시스템** (`ui/icons.py`, QtSvg) — 이모지 대신 테마 틴팅 모노크롬 라인 아이콘.
  하단 네비 / 통계 카드 / 트레이·미니위젯 메뉴 등 전반 적용 (`main.spec`에 `PyQt5.QtSvg` 포함)
- **연장근무 적립 기록 삭제** — 연장근무 관리의 적립 내역 우클릭 → 삭제
  (`Database.delete_overtime_earned`)

### Fixed
- **자동 적립(auto_overtime) OFF가 자동 퇴근 경로에서 무시되던 버그** — 근무일 경계 롤오버 /
  이전일 자동 퇴근 처리도 설정을 존중하도록 게이팅 (`_apply_auto_overtime_gate`).
  (`clock_out` 대화상자 '아니오' 경로는 정상이었음)
- 다크 테마 깨짐: 테이블 세로 헤더·코너 버튼 흰색 누수, 도움말 탭 상단 흰 라인(documentMode),
  트레이/미니위젯 우클릭 메뉴 미적용(검정 글씨) 수정
- 앱 전반 UI 크롬 이모지 제거 + 색상 팔레트 정합 (일일보고/Discord 텍스트는 유지)

### Tests
- `tests/test_overtime_accrual_guard.py` 추가 — 적립 가드 2건(OFF=미적립 / ON=적립) + 적립 삭제 1건

## [2.10.2] — 2026-05-16

### Fixed
- **휴일/주말 근무 시 카운터 초가 항상 `00`** 으로 멈춰 보이던 문제 (사용자 보고)
  - 원인: 휴일 분기에서 `calculate_holiday_overtime`의 분 절삭값(적립 단위)을
    그대로 표시에 사용 → 초 정보 소실
  - 수정: 표시용 `remaining`을 초 정밀도 timedelta로 분리 계산
    (적립 계산은 퇴근 시 분 단위 그대로 — 영향 없음)
  - 차감 항목(점심·저녁·외출·연장 사용)은 `calculate_holiday_overtime`과 동일하게 적용

## [2.10.1] — 2026-05-01

### Fixed — 업데이트 시 cmd 창 깜빡임 제거
- **`updater.spec`**: `console=True` → `console=False` (windowed 빌드).
  자동 업데이트 적용 시 잠깐 뜨던 까만 cmd 창이 더 이상 보이지 않음.
- **`updater.py`**: stderr 출력을 `~/.clockout_logs/updater.log` 파일 폴백으로 전환
  — windowed 모드라도 진단 로그는 보존. 모든 단계(시작/PID 대기/replace/launch)
  에 타임스탬프 + 결과 기록.
- **`updater.py launch()`**: `subprocess.Popen` 에 `CREATE_NO_WINDOW` 플래그 추가
  (DETACHED_PROCESS와 함께) — 자식 프로세스가 콘솔을 새로 만들지 않음.
- **`utils/updater_client.py apply_update()`**: 같은 패턴으로 `CREATE_NO_WINDOW` 추가.
  main.exe → updater.exe 호출 시점에서도 콘솔 생성 차단.

## [2.10.0] — 2026-05-01

### Added — 정부 공휴일 API 자동 동기화
- **공공데이터포털 특일정보 API 연동** (`utils/holiday_api.py`)
  - 한국천문연구원 운영 공식 데이터 — `/getRestDeInfo` 엔드포인트
  - 임시공휴일·근로자의 날까지 정부 공인 데이터로 보강
  - 일일 한도 10,000회 / 사용자 50명 = 0.5% 사용
  - 키는 dev 본인 계정의 특일정보 API 한정 키
- **`Database.add_korean_holidays_from_api(year)`** — 정부 API 1차 시도
- **`add_korean_holidays_auto()` 동작 변경** — 1차 정부 API → 2차 fallback `holidays` 패키지
- **`migrate_v290_holidays_auto_sync`** — 일 1회 자동 동기화 (백그라운드 스레드)
  - sentinel: `settings['holidays_synced_date']`
  - 매일 호출 → 정부가 임시공휴일 발표하면 다음 날 자동 반영
  - 부트스트랩 비차단 (네트워크 호출은 daemon thread)
  - 테스트 환경: `CLOCKOUT_DISABLE_HOLIDAY_SYNC=1` 로 비활성화

### Changed
- 설정 → "한국 공휴일 자동 추가" 버튼 안내문 — 1차 정부 API / 2차 holidays 패키지

### Tests
- `tests/test_holiday_api.py` 14개 신규 (응답 파싱 / 단일/다중 item / 401·timeout / 응답 검증)
- `tests/conftest.py` — 모든 테스트에서 백그라운드 동기화 비활성화
- pytest: 175 → **189**

### 주의
- 키 활용기간 시작 직후엔 백엔드 propagation으로 401 가능 (1~2시간 또는 익일 활성화).
  401 시 fallback (holidays 패키지 + 근로자의 날 명시 추가) 정상 동작 — 사용자 영향 없음.

## [2.9.0] — 2026-05-01

### Fixed — 휴일 hot-path 버그 (사용자 보고)
- **휴일에 출근해도 정상 출근으로 처리되어 추가근무 적립이 안 되던 문제**
  - `update_display()` 1Hz 루프에 `is_non_working_day` 분기 누락으로 휴일에도
    "남은 시간 8h"부터 카운트다운 → 실제 출근 즉시 적립이 시작되지 않음
  - 수정: 출근 직후부터 음수 remaining 표시, "공휴일 근무 (전체 적립)" 그룹 타이틀
  - 진행바: 휴일은 100% 고정 (의미 없음)
  - 예상 퇴근: "휴일 근무 (정해진 퇴근시각 없음)"
- **휴일 "퇴근 30분 전" 알림 게이팅** — 휴일엔 정해진 퇴근시각이 없으니 무의미한 알림 스킵
- **자동복구 퇴근 3곳의 `// 30) * 30` 하드코딩** → 사용자 `overtime_unit` (15/30/60) 설정 적용
- 4곳에 중복되던 휴일 연장 계산 로직을 `TimeCalculator.calculate_holiday_overtime()` 헬퍼로 통합

### Added — 연차 미리등록 + 통합 스케줄 + 반복 연차 (Phase 1+2)

#### Phase 1 — 연차 미리등록 + 자동 적용
- **DB:** `get_leave_minutes_for(date)` / `has_full_day_leave(date)` /
  `get_leave_records_by_date(date)` / `get_leave_records_by_range(start, end)`
- **TimeCalculator:** `effective_work_minutes(date_obj, db)` — 부분 연차만큼 정규 근무 차감
- **종일 연차일 자동 처리:**
  - 자동 출근감지 스킵 (event_monitor 호출 안 함)
  - "🌴 오늘은 휴가" 카드 표시, 카운트다운 제거
  - 메인/미니 위젯/트레이 모두 일관된 휴가 상태 표시
- **종일 연차 + 출근 override:** 휴일처럼 전체 시간 적립 (사용자 확인 후)
- **부분 연차 (반차/반반차/시간):** 기존 leave_used 경로로 카운트다운 단축
- **AddLeaveDialog 검증 강화:** 미래 1년 setMaximumDate / 주말·공휴일 차단 / 같은 날 1일 초과 차단
- **leave_calendar_view:** 예정(파랑) / 사용완료(녹·노·보) 색상 분리

#### Phase 2 — 통합 스케줄 + 반복 연차
- **`recurring_leaves` 테이블** (pattern/leave_type/days/start_date/end_date/memo)
- **`core/recurring_leaves.py`:** weekly / biweekly / monthly 패턴 파서 + expand_for_range/date
- **자동 합산:** `get_leave_minutes_for()` / `has_full_day_leave()`가 반복 패턴 인스턴스도 함께 검사
- **`ui/recurring_leave_dialog.py`:** 매주/격주 요일 또는 매월 N일 입력
- **`ui/schedule_view.py`:** 월간 통합 캘린더 (휴일·연차·반복 색상 구분 + 우클릭 삭제)
- **진입점:** MainWindow.show_schedule(), 트레이 "🗓️ 스케줄", LeaveView "🗓️ 스케줄"

### Changed
- **근로자의 날(5/1) 자동 추가** — `holidays.KR` 패키지가 누락하는 노동자 휴일을
  `add_korean_holidays_auto()`에서 명시적 보강 (매년 반복)

### Tests
- pytest: 122 → **175** (+53)
  - `tests/test_recurring_leaves.py` 32개 (패턴 파싱/매칭/expand/describe)
  - `tests/test_database.py` +12 (TestLeaveQueriesByDate + TestRecurringLeavesDB)
  - `tests/test_time_calculator.py` +9 (TestHolidayOvertime)
- 통합 시나리오: 48 → **53** (+5)
  - S52A 휴일 hot-path / S52B 종일 연차 / S52C 반복 패턴 / S52D 반차 effective / S52E 종일 effective

## [2.8.0] — 2026-05-01

### Added — 도전과제 시스템 + 디자인 리뉴얼
- **🏆 도전과제 시스템** (153개 자동 평가) — 출근·퇴근·연장·연차·식사·외출·계절·시간대·시크릿 등 16개 카테고리.
  - `core/achievements.py`: `Achievement` dataclass + `evaluate_all(db)` + `sync_definitions_to_db(db)`.
  - 5분 throttle로 자동 평가, 신규 잠금 해제 시 시스템 알림 + Discord embed push.
  - `achievements` 테이블 확장: `code`(UNIQUE), `category`, `tier`, `is_secret`, `progress`, `target`, `created_at` 컬럼 추가 (idempotent 마이그레이션).
  - 5단계 등급: 🥉 브론즈 / 🥈 실버 / 🥇 골드 / 💎 플래티넘 / 🌟 레전드.
  - 시크릿 9개 (회문, 잭팟 시각, 13일의 금요일, 7-7-7, 정확 8시간, π day, 피보나치 등).
  - 메타 도전과제 (도전과제 자체 달성 카운트).
- **`ui/achievements_view.py`** — 4탭 다이얼로그 (전체 / 진행 중 / 완료 / 시크릿).
  - 등급별 그라디언트 카드, 진행 게이지, 카테고리 태그, 시크릿 ❓ 처리.
- **자동 hire_date 추적** — 첫 `add_work_record` 호출 시 settings에 자동 기록 (1주년, 365일 후 출근 등 도전과제 활성화).
- **뷰 진입 카운터** — `stat_*_view_count`, `calendar_view_count`, `daily_report_count` 등 8개 settings 키. 도전과제 + 사용 통계용.

### Changed — 다크 테마 디자인 리뉴얼
- **`ui/dark_components.py`** 신설 — 재사용 가능한 다크 디자인 컴포넌트:
  - `dialog_qss()`, `tabs_qss()`, `scroll_qss()`, `button_qss(variant)`.
  - `build_gradient_header()`, `build_stat_card()`, `build_section_card()`.
  - `style_progressbar()`, `transparent_label()` — 글로벌 QSS 충돌 회피.
  - 카드 테마 7종: blue / cyan / green / gold / pink / red / gray.
- **`ui/stats_view.py`** — 4분할 카드 + 차트 섹션 카드. 골드 강조 탭. ghost 닫기 버튼.
- **`ui/help_view.py`** — 다크 톤 + HelpHTML 내부에 다크 CSS 주입 (h1/h2/h3, code, blockquote, table 컬러).
- **`ui/chart_widget.py`** — 모든 matplotlib 차트 다크 테마 적용 (figure/axes facecolor, grid, ticks, legend).
- **온보딩 위저드** — 저녁 분(minutes) 입력 옵션 + i18n화 (Korean/English 프리셋 라벨).

### Fixed — 안정성·일관성
- **타임존 자정 경계 버그**: `has_notification_today`가 `CURRENT_TIMESTAMP`(UTC) vs `DATE('now', 'localtime')` mismatch로 KST 0~9시 사이 알림 중복 발송 가능 — `DATE(sent_at, 'localtime')` 양쪽 적용.
- **DB 연결 누수 가드** — `_conn()` 컨텍스트 매니저 도입, 40+ 메서드 변환 (직접 `get_connection()` 호출 0건).
- **DB 이중 부트스트랩 제거** — `MainWindow(db=None)` 옵션 인자 추가, main.py가 만든 db를 재사용.
- **crash_handler 폴백** — DB 로깅/다이얼로그 단계 분리, 모두 실패해도 `~/.clockout_logs/crashes.log`에 기록.
- **updater PID race window** — 지수 backoff 재시도 (0.3→4.8s, 총 ~9초). Windows Defender 락 해제 대기 충분.
- **Discord URL 형식 검증** — Snowflake ID 17~20자리 + 50+자 토큰 정규식.
- **MealTimeDialog** — 출근 시각 범위 검증 + 야간 출근자 자정 경계 자동 처리.
- **CSV importer/exporter** — `dinner_minutes` 컬럼 round-trip 지원.
- **일일 보고서** — 저녁 섹션 추가 (이전엔 점심만 표시), `break_type` 분리, 자정 경계 처리.
- **저녁 알림** — `check_dinner_reminder()` 신규 (점심 알림과 대칭).
- **알림 임계값 설정화** — 5개 하드코딩 값(점심/저녁 알림 시간, 연장 누적, 주간 한도, 연속 야근)을 settings로 노출.
- **closeEvent 정리** — `aboutToQuit` 시그널로 timer/notifier/tray 정리.
- **마이그레이션 일관성** — 12개 마이그레이션 모두 `_conn()` + try/finally 보장.

### DB 인덱스 (성능)
- `idx_break_records_date_type`, `idx_break_records_date`.
- `idx_overtime_bank_date`, `idx_overtime_usage_date`, `idx_leave_records_date`.

### Tests
- pytest 116개 PASS, 통합 시나리오 44/48 PASS (PyQt 환경 4개 제외).
- 도전과제 한 사이클 시나리오 검증 (30일 시뮬레이션 → 19개 자동 잠금 해제).

## [2.7.0] — 2026-04-30

### Added — 폴리싱 릴리스 (사용자 가시 변화는 작지만 i18n + 테스트 + 구조 개선)
- **i18n 사전 100% 커버리지** — `break_view`, `overtime_view`, `leave_view`,
  `clock_in_dialog` 의 내부 라벨/메시지/플레이스홀더까지 전부 ko/en 키화.
  새 키 50+개 추가 (`view.break.*`, `view.overtime.*`, `view.leave.*`, `dlg.*`).
- **i18n 런타임 재번역** — 재시작 없이 메인 화면 즉시 언어 전환.
  - `ui/i18n_runtime.py`: `register(widget, key)` + `set_language_and_retranslate(lang)`
  - 위젯은 weakref로 보관되어 삭제 시 자동 정리
  - 메인 윈도우 타이틀/하단 메뉴 5개 버튼 등록 완료 (점진 확대)
  - 일부 다이얼로그는 여전히 재시작 필요 (다음 릴리스에서 점진 등록)
- **MealController 분리** — `main_window.py` 에서 점심/저녁 토글·라벨 갱신 로직을
  `ui/controllers/meal_controller.py` 로 추출. 기존 `LockMonitor`/`AutoLunch`/
  `NotificationOrchestrator` 패턴 준수.

### Tests
- 통합 테스트 +15 시나리오 (S36–S52): 온보딩 신규/기존, salary 추정, CSV 가져오기
  (skip/overwrite/overtime 적립까지), notification_log dedupe, meal_record,
  crash_log, updater semver 비교, Discord 입력 검증, goal/accessibility 키 등.
- 신규 pytest 모듈 4종 — `test_salary.py`, `test_csv_importer.py`,
  `test_discord_webhook.py`(network mocked), `test_crash_handler.py`.
- `test_i18n_runtime.py` — register/retranslate/post-callback/dead-widget 정리 검증.
- 총 pytest 케이스: 90 → **122**, 통합 시나리오: 33 → **48**.

### Docs
- `README.md` v2.4–v2.6 신기능 섹션 정리, 재번호.
- `CLAUDE.md` v2.6+ 아키텍처 — 컨트롤러 모듈, 35+ 설정 키, 릴리스 플로우 반영.
- `INSTALL.md` 최신 단일파일 배포 + 온보딩 + 디스코드 + 환경변수 정리.
- `AGENTS.md` 10+ DB 테이블, 컨트롤러 맵, Past Incidents 갱신.

## [2.6.0] — 2026-04-30

### Added — Phase 4 (3종)
- **글꼴 크기 조절** (100% / 125% / 150%) — 설정에서 즉시 반영
- **고대비 모드** — 검정 배경 + 노란 텍스트 (시각약자/야간)
- **코드 서명 인프라** — `release.ps1`에 Authenticode 서명 단계 추가 (옵션)
  - `$env:CODE_SIGN_CERT` (`.pfx` 경로) + `$env:CODE_SIGN_PASS` 환경변수 설정 시 자동 서명
  - signtool.exe 없거나 cert 미설정 시 자동 스킵
  - 코드 서명 인증서 확보 후 활성화하면 SmartScreen 경고 제거 가능

### Settings (신규)
- `font_scale`, `high_contrast`

## [2.5.0] — 2026-04-30

### Added — Phase 3 (4종)
- **주간 자동 리포트** — 월요일 첫 출근 시 (또는 첫 5분 tick) 지난주 요약 발송
  - 시스템 알림 + Discord push (옵션) 동시
  - `notification_log`로 중복 발송 방지
  - 항목: 총 근무·일평균·연장근무·가장 긴 날
- **matplotlib 차트 호버 디테일** — 막대 위에 마우스 올리면 정확한 수치 툴팁
  - 일별 근무 시간 차트(주간 탭)에 적용
- **출근 시각 분포 차트** — 패턴 분석 탭에 30분 단위 히스토그램
  - 평균 출근 시각 빨간 점선으로 표시
- **휴가 캘린더 시각화** — 연차 관리 → "📅 캘린더 보기"
  - 사용 일자에 종일/반차/반반차별 색상 표시
  - 날짜 클릭 → 사용 내역 표시

### Fixed
- `leave_view.py` setLayout 들여쓰기 회귀 수정

## [2.4.0] — 2026-04-30

### Added — Phase 2 (5종)
- **점심/저녁 실제 시간 입력** — 점심/저녁 버튼 우클릭 → 시작·종료 시각 입력 다이얼로그
  - 자동 60분 대신 정확한 분 단위 기록 (`break_records.break_type='lunch'/'dinner'`)
- **캘린더 우클릭 → 과거 일자 추가/편집/삭제**
  - 비어있는 날짜 우클릭: "기록 추가" — 출/퇴근/점심/메모 입력
  - 기록 있는 날짜 우클릭: "편집"/"삭제"
- **월간 목표 설정 + 진행률**
  - 설정 → 월 연장근무 상한 (시간/분) + 일 평균 근무 목표 (시간)
  - 통계 → 월간 탭에 진행률 게이지 (60%/100% 임계 시 색상 변경)
  - 0=비활성 (비활성 시 위젯 자체 숨김)
- **CSV 가져오기** — 표준 포맷 `date,clock_in,clock_out,lunch_minutes,memo`
  - 충돌 정책: 덮어쓰기/건너뛰기/취소
- **자동 Crash Report (Gitea Issues)**
  - 전역 예외 후킹 → crash_log 저장 + 사용자에게 다이얼로그
  - "복사" / "Gitea에 보고" (PAT 옵션) — issue 자동 생성

### Settings (신규 4개)
- `goal_overtime_max_monthly`, `goal_avg_hours_daily`
- `gitea_feedback_token`, `gitea_feedback_enabled`

## [2.3.3] — 2026-04-30

### Fixed
- **Discord 웹훅 항상 실패하던 문제** (Cloudflare error 1010 / 403 Forbidden)
  - 원인: Python 기본 User-Agent(`Python-urllib/3.x`)를 Discord/Cloudflare가
    봇으로 인식해 차단
  - 수정: `utils/discord_webhook.py`에 브라우저 UA 헤더 추가
    (`Mozilla/5.0 (Windows NT 10.0; Win64; x64) ClockOutCalculator/2.3`)
  - 이제 온보딩 위저드의 "테스트 메시지 보내기" + 출퇴근/휴식 push 모두 정상 동작

## [2.3.2] — 2026-04-30

### Fixed
- **도움말 다이얼로그가 빈 창으로 표시되던 치명적 버그**
  - v2.3.1에서 "온보딩 다시 보기" 버튼 추가 시 `self.setLayout(main_layout)` 라인이
    실수로 `_reopen_onboarding` 메서드 안으로 들여쓰기되어 init_ui가 setLayout 없이 종료
  - F1 누르면 빈 화면만 나옴 (탭 6개 모두 미렌더링)
  - setLayout 호출 위치 복원 → 정상 표시

## [2.3.1] — 2026-04-30

### Fixed
- **도움말 다이얼로그에 "온보딩 다시 보기" 버튼 추가**
  - v2.3.0에서 `show_onboarding()` 메서드는 만들었지만 UI 진입점 누락
  - 이제 도움말 (F1) → 좌측 하단 "🚀 온보딩 다시 보기" 버튼으로 위저드 재실행 가능
  - 본인 검증/설정 변경 시 유용

## [2.3.0] — 2026-04-30

### Added — Phase 1 + E1 (소비자 친화 6종)
- **첫 실행 온보딩 위저드** (강제) — 5단계: 환영 → 근무패턴 → 출근 감지 방식 → 연차/시급(옵션) → Discord(옵션) → 완료
  - 신규 사용자: 자동 표시 / 기존 사용자(work_records 있음): 자동 완료 처리
  - "도움말 → 온보딩 다시 보기" 메뉴로 언제든 재실행 가능
- **시급 → 추정 급여** (옵션) — 포괄임금이 아닐 때만 활성화
  - 통계 화면 월간 탭에 "이번 달 추정 급여" 카드
  - 퇴근 후 "오늘 요약" 카드에도 추정 급여 표시
  - 연장수당 가산률 1.0 / 1.5 / 2.0 선택
- **출퇴근 시각 인라인 편집** — 메인 화면 출근/퇴근 라벨 클릭 → 즉시 수정 다이얼로그
- **퇴근 후 "오늘 요약" 카드** — 메인 화면 상단에 총 근무/점심/외출/연장/추정급여 표시
  - 다음 출근 시 자동 숨김 / X 버튼으로 수동 닫기
- **장시간 근무 휴식 권고 알림** — 연속 N시간(기본 4시간) 자리 비움 없으면 "🌿 잠시 일어나세요" 토스트
  - 5분 throttle + 일 1회 가드 (notification_log 테이블)
- **Discord 웹훅 알림** (옵션) — 출퇴근/휴식권고 모바일 push
  - 봇 등록·서버 운영 0. 채널 웹훅 URL만 입력
  - 출근(녹색) / 퇴근 정시(파랑) / 퇴근 연장(주황) / 건강경고(분홍) embed
  - 온보딩에서 즉시 활성화 + "테스트 메시지" 버튼

### Database
- `break_records.break_type` 컬럼 추가 ('break' / 'lunch' / 'dinner' 구분)
- `notification_log` 테이블 신규 (channel, event_type, sent_at, success — 중복 발송 가드 + 통계용)
- 기존 사용자 `onboarding_completed` 자동 true 처리 마이그레이션

### Settings (신규 11개)
- `onboarding_completed`, `salary_enabled`, `hourly_wage`, `overtime_rate`
- `health_break_enabled`, `health_break_hours`
- `discord_webhook_url`, `discord_notif_clock_in`, `discord_notif_clock_out`, `discord_notif_health`

## [2.2.4] — 2026-04-30

### Added
- **`auto_overtime` 옵션 실제 동작** — 기존엔 UI만 있고 동작 안 했음
  - **OFF**: 퇴근 시 적립 가능한 분이 있으면 "적립할까요?" 다이얼로그 표시 (Y/N)
  - **ON** (기본): 자동 적립 (이전과 동일)
- **`overtime_unit` 실제 동작** — 30/15/60분 단위 적립 선택 반영
  - 기존엔 무조건 30분 절삭. 이제 설정값 사용 (`time_calculator.calculate_overtime` `unit_minutes` 파라미터)
- **퇴근 알림 시점 사용자 설정** (`notification_before_minutes`)
  - 기존 30분 하드코드 → 1~120분 SpinBox로 사용자 지정
  - 설정 → 알림 그룹에 "퇴근 알림 시점" SpinBox

### Removed (죽은 옵션 정리)
- `auto_detect_boot` — 부팅 감지 비활성화 모드. 어디서도 안 쓰임
- `notification_enabled` — 마스터 스위치. 4개 개별 NOTIF_* 키로 충분
- `annual_leave_used` — `leave_balance`로 대체된 레거시 카운터

### Fixed
- 죽은 옵션이 사용자에게 "동작하는 것처럼" 보이던 신뢰성 문제 일괄 해소

## [2.2.3] — 2026-04-30

### Fixed
- **`updater.exe` 임베딩 빌드 안정화**
  - v2.2.2에서 `main.spec --clean`이 빌드 시작 시 `dist/updater.exe`를 지우면서
    임베딩이 누락되던 문제 수정
  - `release.ps1`이 `dist/updater.exe`를 `build/staging/`로 복사 후 main.spec이 그걸 참조
  - main.exe 사이즈가 다시 ~78MB (updater 내장) 로 정상화

## [2.2.2] — 2026-04-30

### Added
- 윈도우 제목에 현재 버전 표시 — 자동 업데이트 후 신/구 버전 시각적 구분
  - 예: `⏰ 퇴근시간 계산기 v2.2.2`

### Test
- 자동 업데이트 더미 릴리스 — 단독 main.exe 배포 + 자가 업데이트 흐름 검증용

## [2.2.1] — 2026-04-30

### Added
- **`updater.exe` 내장 (Single-file 배포)**
  - `main.exe` 안에 `updater.exe`를 PyInstaller `datas`로 임베드
  - 시작 시 `main.exe`가 같은 폴더에 `updater.exe`를 자동 추출 (없거나 사이즈 다르면 갱신)
  - 사용자는 이제 **`main.exe` 하나만 받아도** 자동 업데이트 동작
  - 업데이트로 `main.exe`가 교체되면 새 main 실행 시 새 updater.exe도 자동 갱신
- `utils/updater_client.py`: `_MEIPASS` fallback — 권한 부족 등으로 추출 실패 시 TEMP에서 동작

### Changed
- `release.ps1` 빌드 순서: updater 먼저 → main 나중 (datas 의존성)
- `main.spec`: `dist/updater.exe` 존재 시 자동 임베드 (조건부 datas)

### Fixed
- 한국어 릴리스 노트 인코딩 (`Get-Content` ANSI → `[System.IO.File]::ReadAllText` UTF-8)

## [2.2.0] — 2026-04-30

### Added
- **자동 업데이트** — Gitea Releases 기반
  - 시작 5초 후 백그라운드 버전 체크 (silent)
  - F5 또는 설정 → 데이터 관리 → "업데이트 확인" 버튼으로 수동 트리거
  - 새 버전 발견 시 다운로드 → `updater.exe`가 메인 종료 대기 → 파일 교체 → 재시작
  - 실패 시 .bak 자동 롤백
- **`core/version.py`** — `__version__` 상수
- **`updater.py` + `updater.spec`** — 독립 자가 업데이터 (Python 표준 라이브러리만, 6MB)
- **`utils/updater_client.py`** — Gitea/GitHub 호환 Releases API 클라이언트
- **`release.ps1`** — 로컬 원클릭 릴리스 스크립트 (Runner 불필요)
  - 빌드 + 태그 push + Gitea Release 생성 + 자산 업로드 자동화
  - CHANGELOG에서 릴리스 노트 자동 추출
  - `--DryRun` / `--SkipTests` 옵션

### Changed
- `Settings → 데이터 관리`에 "버전 표시 + 업데이트 확인" 추가
- `main.spec` datas에 안내 주석 (updater.exe는 별도 배포)

## [2.1.0] — 2026-04-29

### Removed
- **Claude AI 분석 기능** 제거 — 외부 API 의존성 정리
- **로컬 HTTP API** 제거 — 외부 위젯 연동 기능 미사용으로 정리
- **pandas 의존성** 제거 — 코드에서 미사용 (PyInstaller 빌드 사이즈 감소)

### Added
- **첫 잠금 해제 = 출근 옵션** (`clock_in_on_unlock`) — PC를 안 끄는 사용자 케이스 해결
- **WAL 모드 + busy timeout** — 클라우드 동기화 환경에서 다중 PC 동시 접근 안전성↑
- **`MainWindow` 컨트롤러 분리** — `LockMonitor` / `AutoLunchManager` / `NotificationOrchestrator`
- **`update_display` 변화 감지** — 직전 값과 동일 시 `setText` 스킵 (1Hz hot-path 부하↓)
- **언어 변경 시 자동 재시작 제안** — 사용자 동의 후 `os.execv`로 재시작
- **`CHANGELOG.md`** + **GitHub Actions CI** 추가

### Changed
- 5분 throttle 알림 (건강/주간/누적) `_last_5min_bucket` 명시 가드
- `PyInstaller` `excludes`에 `pandas`, `numpy.testing`, `PyQt5.QtWebEngineWidgets` 추가

## [2.0.0] — 2026-04-29

### Added
- **단축근무 지원**: `work_minutes`(분 단위) + 5종 프리셋 (8h / 7h30m / 7h / 6h / 4h)
- **자동 점심시간** 적용 (출근 4h 경과)
- **DB 자동 백업** (`~/.clockout_backups/`, 7일 회전, SQLite backup API)
- **화면 잠금 자동 외출/복귀**
- **공휴일 자동 등록** (`holidays` 패키지, 음력 명절 포함)
- **시스템 트레이 완전 통합** (점심/외출/통계/캘린더/도움말)
- **미니 위젯** (Always-on-top, 드래그 가능)
- **matplotlib 차트** (일별/요일별 근무시간)
- **건강·주간·누적 알림** (3종 추가)
- **i18n 인프라** (한국어/English, 28 카테고리, HelpView ko/en HTML)
- **설정 키 상수 모듈** (`core/settings_keys.py`)
- **DB 경로 override** (클라우드 동기화 폴더 지정 가능)
- **앱 단축키 7종** (Ctrl+O/L/D/B, F1, Ctrl+R, Ctrl+,)
- **사용 설명 가이드** (HelpView, 6 탭)
- **48 통합 시나리오** + 5 i18n GUI + 8 widget smoke 테스트

### Fixed
- **+29h remaining time bug** — `break_minutes`에서 overtime 차감하던 로직 수정
- **annual_leave_total / annual_leave_days 키 불일치** — 양방향 자동 동기화

## [1.0.0] — 2026-01-09

- 최초 릴리스: 자동 출퇴근 / 점심 관리 / 30분 단위 연장근무 적립 / 캘린더 / 통계
