532 Commits

Author SHA1 Message Date
songyc macbook
fef25f30d0 docs(claude): future-dated records excluded from streaks is intended
- §6.4 연속 달성에 명시: 미래 시각 기록(수동 입력)은 진행률·게이지에 즉시 반영되지만 연속 계산은 now까지만 집계 — 선기록으로 연속이 미리 쌓이지 않게 하는 의도된 동작(사용자 결정, 고치지 말 것)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-16 02:53:44 +09:00
songyc macbook
fced5d1958 fix(l10n): localize widget/complication placeholder sample labels
- placeholder()의 생 한국어 리터럴 6곳을 String(localized:)로: ③다짐 링·④다짐 현황·잠금화면 위젯의 spanLabel "오늘"(기존 키 재사용), 워치 목표/다짐 달성률 "목표"/"하루"(기존 키 재사용), 워치 현재 현황 샘플 "독서"(워치위젯 카탈로그 신규 1키 — en Reading/ja 読書, iOS 위젯 카탈로그와 동일 값)
- placeholder는 갤러리/로딩 스켈레톤 전용 경로라 타임라인 예산·버튼 반응성·갱신 경로 무영향 — ①⑤ 위젯 placeholder가 이미 쓰던 패턴과 동일
- 검증: Debug·Store·워치 3스킴 빌드 성공, 전 카탈로그 missing/stale 0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-16 02:53:43 +09:00
songyc macbook
2b6ca94f09 docs(help): add short-session-ignore and note-prompt topics
- 모음 탭 그룹에 도움말 2개 추가: '짧은 기록 무시'(측정 시작·종료 다음 — 3초~1분 기준, 위젯·워치·시리 종료에도 적용됨을 안내), '기록에 메모 남기기'(길게 누르기 메뉴 다음 — 행동별 토글·건너뛰기·기기 로컬 저장·기록 탭 노란 표시·수정 시 메모 편집 안내)
- 새 문자열 3키 en/ja 번역 채움 (설정 항목명은 기존 카탈로그 번역 'Ignore short sessions'/'Show note prompt when recording' 그대로 인용, 제목 '짧은 기록 무시'는 기존 키 재사용)
- 검증: -helpPreview ko/en 스크린샷 — 두 주제 모두 의도한 위치에 렌더

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-16 02:53:43 +09:00
songyc macbook
0c87375d53 docs(claude): add missing -widgetRenderMode arg + seedDemo streak precondition
- §14 설정/기타 행에 코드에는 있으나 표에 빠져 있던 -widgetRenderMode accented|vibrant 보충
- §14 seedDemo 기대값(아침 루틴 연속 1일)에 실행 시각 전제 명시 — 시드 기록이 고정 시각이라 오전 7:30 이전 실행 시 연속 0일이 정상 (전수 점검 중 새벽 실행에서 확인)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-16 02:38:05 +09:00
songyc macbook
7a6731a1f5 fix(l10n): wrap four raw Korean literals missed by catalog extraction
- 기록 탭 목록의 진행 중 세션 표기 "진행 중"이 문자열 보간 안 String 리터럴이라 영어·일본어 UI에 한국어로 노출되던 것 수정 — String(localized:)로 기존 카탈로그 키 재사용 (시뮬레이터 en "In progress" / ko "진행 중" 스크린샷 검증)
- 같은 패턴 3곳 동일 수정: 꼬리표 편집 미리보기 행 "미리 보기"(String 삼항), 시리 다짐 엔티티 부제 "목표 없음"(?? 폴백), 워치 행동 목록 제목 "꼬리표 없음"(String 파라미터) — 전부 기존 번역 키 재사용이라 카탈로그 변경 없음(xcstringstool sync 무변화 확인)
- 검증: Debug·Store·워치 3스킴 빌드 성공, 시리 인텐트 스모크 테스트 10/10, streak-dump 수치 손검산 일치

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-16 02:38:05 +09:00
songyc macbook
063a7a2d7f docs(claude): record release-freeze status + watch simulator UDIDs
- §1에 '현재 상태 (2026-07-16)' 행 추가 — 기능 동결, RC 커밋 f05e611, 실기기 이상 없음, 사업자 승인 대기
- §2.2 자주 쓰는 시뮬레이터에 워치 UDID 2종(watchOS 11.5 / 26.2) 기재
- 문서 버전 v2.1 (2026-07-16)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-16 02:12:56 +09:00
songyc macbook
f05e6113a1 chore(release): privacy manifests for all 4 bundles + lower minimum OS to 26.0/10.0
유료 앱 출시 패키지 점검에서 발견한 두 가지 보완:

1) PrivacyInfo.xcprivacy 추가 (iOS 앱·위젯 확장·워치 앱·워치 위젯 확장) —
   required-reason API 선언(ITMS-91053 대응): UserDefaults 카테고리에
   CA92.1(앱 자체 설정) + 1C8F.1(App Group 공유), 수집 데이터·트래킹 없음
   (ASC '데이터 수집 안 함' 선택과 일치). 파일 타임스탬프·부팅 시간·디스크
   용량·키보드 목록 API는 전수 grep으로 미사용 확인. 앱 타깃이 위젯 폴더를,
   워치 앱 타깃이 워치위젯 폴더를 동기화(미리보기 화면용)하고 있어 매니페스트가
   이중 복사되던 충돌은 membershipException 2건으로 해소 — 번들마다 자기
   매니페스트 1개씩 포함됨을 빌드 산출물로 확인.

2) 배포 타깃 하향: iOS/iPadOS 26.5 → 26.0 (리퀴드 글라스 glassEffect가 26.0
   하한이라 그 이상 낮출 수 없음), watchOS 26.0 → 10.0. 컴파일러 availability
   검사가 오류 0건으로 통과 = 해당 버전 지원 보증. 검증: Debug·Store·워치 스킴
   빌드 성공, MinimumOSVersion 각인(26.0/10.0) 확인, watchOS 11.5 시뮬레이터
   실기동(기존 최소치로는 설치 불가였던 OS) + 26.2 시뮬레이터 실기동, 아이폰
   시뮬레이터 부팅 확인. Series 4·5·SE(1세대) 등 watchOS 10~11 워치 사용자와
   iOS 26.0~26.4 기기가 지원 범위에 들어옴.

CLAUDE.md §1에 최소 OS와 매니페스트 완비 상태 기록.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 19:05:09 +09:00
songyc macbook
6738fe4b7e docs(claude): watch verification route + widget ③ deadline-value note
출시 전 최종 점검에서 발견한 문서 어긋남 정리:
- §2.1/2.2: 워치·위젯 개별 스킴 존재 명시 + 워치 UI 검증 방법(워치 스킴 →
  watchOS 26 시뮬레이터 + -complicationPreview) 추가 — 이번에 실제 검증한 경로
- §10 ③: 누적값 라벨 설명에 마감 시각 다짐의 창 안 값(spanRawValue) 예외 반영

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 18:48:49 +09:00
songyc macbook
01399afa2c chore(debug): -questShowEditor launch arg — open quest editor headlessly
다짐 편집기를 여는 검증용 런치 인자 추가 (DEBUG 전용, §14 관례).
마감 시각이 있는 다짐을 우선 열어 새 마감 UI(토글·시간 선택·footer)의
스크린샷 검증이 CLI로 가능해진다. 없으면 첫 다짐의 편집기를 연다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 18:26:58 +09:00
songyc macbook
6a486a4fbd feat(quest): daily quest deadline time — clip the day's measurement window
하루 단위 다짐에 '마감 시각까지만 집계' 옵션 추가 (최소판 설계):

- 모델: Quest.deadlineMinutes(-1=없음) 필드 1개 — CloudKit 안전(기본값), 주기가
  하루 단위가 아니면 값이 남아도 무시(hasDeadline이 주기·범위를 함께 검사)
- 의미론: 마감 = 그날 집계 창의 끝. dayMeasurementRange(forKey:) 단일 구현을
  주기 범위(current/judgment)·주/월 span 하루 루프(spanValue)·연속 버킷팅
  (perDayValues)·위젯 누적 라벨(spanRawValue)·일기 카드가 공유한다.
  원본 기록은 불변 — 마감 뒤 기록은 남되 그 다짐에만 미집계. 시간형은 기존
  겹침 클리핑이 마감을 걸친 세션을 부분 인정(추가 코드 0). '이하 유지'는
  마감까지 한도 안이면 그날 달성으로 고정.
- 경계 규칙: 마감은 그 논리적 하루(24h) 안에서 그 시계 시각의 등장 지점 —
  하루 시작보다 이른 시각은 다음 달력일 새벽, 시작과 같은 시각은 +24h(온전한
  하루)로 0길이 창이 수학적으로 불가능(에디터 검증 불필요, 오류 상태 없음)
- 의도된 최소화: '마감 지남' 전용 표시 없음(게이지 값이 마감 시점에 연속이라
  위젯 타임라인 마감 경계 불필요 — 위젯·워치·시리는 코드 변경 0으로 자동 일관),
  연속 달성은 값 클리핑만(오늘 놓친 끊김 반영은 내일부터 — 유예 규칙 유지),
  주간/월간/기간 마감은 미지원
- UI: 다짐 에디터 토글+시간 선택(하루 단위만, footer 설명), 주기 문구에
  "· 오전 8:00까지" 표기(목표 탭·시리 요약·CSV 자동 반영), 일기 카드
  "하루 목표 · 오전 8:00까지", 도움말 항목 추가, ko/en/ja 번역
- 검증: seedDemo에 마감 다짐 시드(창 안/밖/어제 실패 케이스) — 하루 100%
  (창 밖 기록 포함 시 200%가 되는 함정 통과), 주간 29%(=2/7)·월간 6%(=2/31)
  수기 계산과 정확 일치, streak-dump 연속 1일(어제 실패로 끊김) 확인,
  마감 없는 다짐 전부 기존 수치 유지(경로가 hasDeadline 가드로 완전 동일),
  일기 카드 1회/1회(원본 합계는 그대로), 영어 로케일 "By 8:00 AM" 렌더,
  Debug·Store 빌드 성공, 카탈로그 9종 missing/stale 0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 17:52:18 +09:00
songyc macbook
b399ee02fe refactor(history): unify calendar popover date mapping with startOfDay-as-key rule
기록 탭 달력 팝오버의 선택일 변환을 정오 경유(dayKey(for: 12:00))에서
"선택 달력일의 startOfDay를 키로 직접 사용"으로 통일 — 일기 내보내기 수정과
같은 규칙(CLAUDE.md §4.1). 현실적 설정(하루 시작이 자정~오전)에서는 동작이
완전히 동일하고, 하루 시작이 오후로 설정된 엣지에서만 전날로 밀리던 것이
고른 날짜 그대로로 바로잡힌다. 이로써 정오 매핑 패턴은 코드에서 사라짐.

검증: 하루 시작 00:00/06:00/18:00 수치 비교(앞 둘 동일, 18:00만 7/14→7/15로
교정), Debug·Store 빌드 성공, 기록 탭 렌더 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 16:21:05 +09:00
songyc macbook
28e649c2de chore(git): untrack Expiranner xcuserdata (already gitignored)
루트 .gitignore의 **/xcuserdata/ 규칙보다 먼저 커밋돼 계속 추적되던
Expiranner의 Xcode 사용자 상태 파일(UserInterfaceState.xcuserstate,
xcschememanagement.plist)을 인덱스에서만 제거 — 로컬 파일은 유지되고
이후 변동이 git status에 잡히지 않는다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 16:07:46 +09:00
songyc macbook
05b2d392c8 fix(diary): calendar month drift + export date picks off-by-one at custom day start
'하루 시작 시간'이 자정보다 늦게 설정된 경우의 논리적 하루 경계 버그 2건:

- 달력: monthAnchor(자정 키)를 monthRange(containing:)에 그대로 넣어
  매달 1일에 이전 달 그리드가 그려지고 헤더(현재 달)와 불일치했다.
  키를 dayRange(forKey:).lowerBound로 실제 시각으로 변환해 전달
  (기록·통계 탭이 쓰는 기존 패턴). monthSummary 카운트도 동일 수정.
- 내보내기: '날짜 지정'(MultiDatePicker)의 선택일(자정 Date)을 dayKey(for:)에
  넣어 전날 일기가 내보내졌다. DiaryEntry.dayKey가 "달력일의 자정"이므로
  선택 달력일의 startOfDay를 키로 직접 사용 — 하루 시작 시간이 몇 시든 안전.
  '구간 지정'도 같은 원리로 통일(표시된 달력일 = 내보내는 일기 날짜 보장).

CLAUDE.md §4.1에 재발 방지 규칙 추가: 자정 키를 dayKey(for:)/~Range(containing:)에
재투입 금지, 달력 UI 선택일은 startOfDay를 키로 직접 사용.

검증: 하루 시작 00:00/06:00/18:00 전부 수치 검증(수정 전 06:00에서 이전 달·전날
재현 확인), Debug·Store 빌드 성공, iPad 시뮬레이터에 하루 시작 06:00 주입 후
달력(7월 헤더·그리드 일치, 마커 2건)과 내보내기 시트("일기를 쓴 날 2일") 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 16:07:05 +09:00
songyc macbook
3f518aa91b chore(l10n): purge stale catalog entries + per-target stringsdata sync routine
전체 코드 점검 중 발견한 번역 카탈로그 정리:
- IOS 카탈로그의 stale 항목 2건 삭제 — "다른 행동 %lld개도 함께 추적 중"은
  위젯 타깃 전용 문자열(위젯 카탈로그에 번역 완료 상태로 존재)이고,
  "프리미엄 상태 (테스트)"는 DEBUG 전용 문구
- DEBUG 프리미엄 토글 라벨을 Text(verbatim:)으로 — 개발용 문구는 카탈로그로
  추출하지 않는 컨벤션(§15-6)에 맞춤 (같은 섹션의 다른 문구와 동일)
- CLAUDE.md l10n 루틴 수정: 카탈로그마다 자기 타깃의 stringsdata만 sync하도록
  경로 필터 추가 — 전 타깃을 한꺼번에 넣으면 위젯 전용 문자열이 IOS 카탈로그에
  미번역으로 재유입되던 오염의 근본 원인 제거 (이번 stale의 발생 경로)

검증: 전 카탈로그(9개) missing/stale 0건, 카탈로그 의미 diff = stale 2건 삭제뿐
(번역 변경 없음), Debug·Store 스킴 빌드 성공

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 15:52:10 +09:00
songyc macbook
aa1c098d98 chore(l10n): Xcode re-save of string catalog — formatting + stale marks
- 키 정렬/공백 스타일 정규화 (Xcode 자동 재저장)
- 미사용 문자열 2건에 extractionState: stale 표시
- 번역 내용 추가/삭제 없음

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 02:56:13 +09:00
songyc macbook
3067877d37 fix(diary): make full template row tappable in page chooser sheet
페이지 추가 시트의 '내 양식' 목록에서 한 쪽짜리 양식(사진·1쪽 PDF) 행이
왼쪽(썸네일·이름)만 눌리고 오른쪽 절반은 반응하지 않던 문제 수정.
DiaryTemplateRowLabel이 내용물 폭만 차지해 plain 버튼의 contentShape도
그 좁은 라벨에만 적용됐던 것 — 라벨을 frame(maxWidth: .infinity)로 행
전체 폭으로 넓힌 뒤 contentShape(Rectangle())를 적용해 행 어디를 눌러도
동작하게 했다. 여러 쪽 PDF 행(NavigationLink)은 원래 전체 폭이 눌려
변경 없음.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 01:48:09 +09:00
songyc macbook
1e50485537 fix(widget): quest ring tap dead zone — drop invalidatableContent from shape subtree
③ 다짐 진행률 위젯에서 링·중앙 아이콘 영역을 누르면 인텐트 대신 앱이
열리던 실기기 버그 수정. 원인은 링 ZStack(도형+아이콘)에 붙인
invalidatableContent() — invalidatable로 표시된 래스터 서브트리는 별도
레이어로 분리되며 Button(intent:)의 히트 영역에서 빠져, 셀 단위
contentShape(.rect)로도 살아나지 않는다. 정상 동작이 확인된 ① 위젯과
같은 패턴(텍스트 값에만 invalidatableContent)으로 통일하고, 탭 접수
피드백은 퍼센트·누적값 줄이 계속 담당한다. CLAUDE.md §10에 도형·아이콘
ZStack 금지 규칙 추가.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-15 01:42:36 +09:00
songyc macbook
9c9c24f7df feat(a11y): VoiceOver labels for icon-only buttons + combined action-cell narration
접근성 감사 ③ — 전 화면의 아이콘 전용 버튼과 텍스트 없는 시각
요소에 보이스오버 라벨 부여 (시각 UI 변화 없음):

- 모음 탭: 행동 버튼 셀을 label(이름)+value(측정 중/오늘 누적)로
  한 덩어리 낭독 — 아이콘·타이머가 따로 읽히지 않음. 진행 중 행의
  정지 버튼 "측정 종료".
- 기록 탭: 날짜 이동 화살표(이전/다음 날짜), 내보내기, 기록 필터,
  필터 칩 해제 버튼. 타임테이블의 색 블록·횟수 점은 "행동 이름 +
  시각(구간)" verbatim 라벨로 낭독 가능하게.
- 통계 탭: 기간 이동 화살표(이전/다음 기간), 내보내기, 통계 필터,
  필터 해제.
- 행동/꼬리표/목표 탭: + 추가 버튼(행동/꼬리표/목표 추가), ⋯ 메뉴.
- 일기: 달 이동 화살표(이전/다음 달), 내보내기, ⋯ 메뉴, 기분 타일
  ("오늘 기분"), 할 일 삭제 버튼.
- ⑥ 현재 진행 중 위젯: 종료 버튼 "측정 종료".
- CLAUDE.md §15에 컨벤션 추가: 아이콘 전용 버튼 accessibilityLabel
  필수, 복합 셀은 label+value 한 덩어리, 텍스트 없는 시각 요소는
  verbatim 라벨.

신규 문구 9종 ko/en/ja 번역 + 위젯 카탈로그 '측정 종료' 번역 복사
(전 카탈로그 missing 0/stale 0). Debug·Store 빌드 성공, 접근성
수식어는 시각 렌더링에 영향 없음(스모크 런치 확인). 실기기에서
보이스오버 켜고 핵심 흐름(행동 시작/종료·날짜 이동) 낭독 확인 권장.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 22:24:21 +09:00
songyc macbook
747aab109d perf(history,stats,diary): range-bounded queries + one-pass stat buckets; declare encryption exemption
배포 잔여 ①-(2) + 성능 백로그 ② 처리:

1) ITSAppUsesNonExemptEncryption=NO (IOS/Info.plist)
   OS 표준 암호화(HTTPS·iCloud)만 사용 — 면제 선언으로 업로드마다
   뜨는 수출 규정 질문 생략. 빌드 산출물 plist에서 키 확인.

2) 기록·통계·일기 기록 조회를 날짜 범위로 제한 (전량 로드 제거)
   예전에는 4개 화면(기록/통계/일기 요약/일기 내보내기)이 모든
   TimeSession·CountEntry를 @Query로 올려 메모리에서 걸렀다 —
   기록이 수만 건으로 쌓이면 탭 진입·날짜 이동이 느려지는 구조.
   - HistoryRecordsView 신설: 선택 날짜가 속한 '주' 범위만 조회
     (목록·타임테이블 두 모드를 한 쿼리로 커버). 하루 경계 걸침·
     진행 중 세션은 "시작 < 범위끝 AND (종료 없음 OR 종료 > 범위
     시작)" 겹침 조건으로 포함. 날짜가 바뀌면 새 조건으로 재생성
     (SwiftData 동적 쿼리 패턴). 내보내기 시트도 이 범위 데이터 사용.
   - StatsChartsView 신설: 기간(하루/주/월) 범위 조회 + **1회 버킷
     집계**(StatsData) — 조회된 기록을 splitByDay로 한 번만 순회해
     행동별·하루별 버킷을 만들고 모든 카드(꺾은선·막대·표·꼬리표
     집계)가 공유. 카드마다 Aggregator로 전체 기록을 재순회하던
     O(카드×행동×기록) 비용 제거. 집계 규칙은 기존과 동일.
   - DiarySummaryPage: 해당 날짜 범위만 조회하는 init 추가.
   - DiaryExportSheet: 전량 @Query 대신 렌더 대상 날짜별 on-demand
     fetch로 전환.

3) -seedBulk DEBUG 인자 신설: 과거 500일 약 1만 건(세션 2,032 +
   횟수 8,039 실측) 생성 — 규모 검증용.

검증: 변경 전/후 스크린샷 비교 — 주간 타임테이블(자정 걸침 세션
포함)·월간 통계 차트가 픽셀 단위 동일(집계 등가성), 벌크 1만 건
상태에서 월간 통계 즉시 렌더 + 벌크 시리즈 정상 표시. Debug·Store
빌드 성공, 카탈로그 missing 0/stale 0(신규 문구 없음). CLAUDE.md
§1·§6.5·§6.6·§14 갱신.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 22:07:16 +09:00
songyc macbook
09d0b86ebc feat(premium): privacy policy + terms links on the paywall (review guideline 3.1.2)
배포 잔여 ①-(1) 처리 — 자동 갱신 구독 심사 필수 요건:

- Docs/PRIVACY.md 신설: 개인정보처리방침 ko/en/ja 3개 국어.
  핵심 내용 — 개인정보 수집 없음(서버·계정·광고·분석 SDK 없음),
  모든 데이터는 기기 + 사용자 본인의 iCloud 비공개 DB에만 저장
  (개발자 접근 불가), 결제는 App Store 처리, 권한(캘린더·사진·
  알림) 용도 명시, 데이터 삭제 방법, 문의처 songyc.eng@gmail.com.
  → github.com/notalentprogrammer/HaruDanim 저장소 main 브랜치에
  PRIVACY.md로 업로드하면 앱에 박힌 링크가 활성화된다.
- 결제 화면(PremiumView)에 '약관과 정책' 섹션 추가:
  개인정보처리방침(위 GitHub URL) + 이용약관(Apple 표준 EULA —
  커스텀 EULA 미제공 시 자동 적용되는 표준 계약으로 갈음, 3.1.2 허용).
  외부 링크 화살표 표시, 신규 문구 4종 ko/en/ja 번역.
- CLAUDE.md §1 배포 잔여 갱신 (남은 코드 측 작업은
  ITSAppUsesNonExemptEncryption 1건, ASC 등록 시 앱 설명에도
  두 링크 기재 + 앱 개인정보 보호 '데이터 수집 안 함' 선택 메모).

검증: Debug·Store 빌드 성공, iPad 시뮬레이터 결제 화면에서
섹션·링크·푸터 표시 확인. 카탈로그 missing 0 / stale 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 21:43:04 +09:00
songyc macbook
fd4e198fe4 fix(goal,diary): pro-rated mid-period judgment, day-key edge, template render retries
재분석에서 나온 잔여 항목 일괄 처리:

1) 판정 하루 키 엣지 (A)
   judgmentReference가 종료일 '정오'를 경유해 논리적 하루를 찾던
   것을 달력일 키 직접 조회로 변경 — '하루 시작 시간'을 12시 이후로
   설정한 경우에도 판정 범위가 전날로 밀리지 않는다.

2) 주기 중간 종료 판정 (B — 설계 결정: 경과일 비례)
   종료 판정 전용 QuestProgress.judgment(asOf:) 신설, 판정 평균
   (questAchievementRatio)이 current() 대신 사용:
   - 값은 기준 시점까지의 누적으로 제한 — 종료일 이후 같은 주기의
     기록이 판정에 섞이지 않음
   - 주간/월간/특정 기간 다짐의 목표량·한도를 주기 내 경과일 비율로
     축소 — 수요일에 끝나는 목표의 주간 다짐이 남은 4일치 미달로
     깎이지 않고, '이하 유지' 한도도 같은 비율이라 대칭적으로 공정
   - 하루 단위 다짐은 축소 없음 (그날 목표는 온전히 채워야 달성)
   - 수동 종료(manualFinish)도 같은 함수를 타므로 주중 수동 종료의
     주간 다짐이 공정하게 판정됨. 표시 연산(combinedSpanRatio·
     spanProgress)은 불변.
   검증: 주간 2시간 다짐 목표를 주 시작일(월)에 종료 →
   화요일 기록 40분이 배제되고 비례 목표(≈17분) 기준으로 판정됨을
   DB에서 확인 (처음 실패는 '설치 직후 첫 실행 인자 무시' 시뮬레이터
   특성이 원인 — 코드 문제 아님, §2.2 문서화된 사항).

3) 양식 렌더 트레이드오프 (C)
   - 백그라운드 렌더 실패 시 renderedScale을 되돌려 다음 기회(줌·
     재표시)에 재시도되게 함
   - 썸네일 겸 첫 표시 플레이스홀더 해상도 320→512px — 본 렌더
     도착 전의 흐릿함 완화 (저장 증가는 수십 KB 수준)
   - 양식 관리의 사용 중 페이지 수를 행마다 재스캔하지 않고 1회 집계

빌드: Debug·Store·워치 3스킴 성공. CLAUDE.md §4.3에 주기 중간
종료 판정 규칙 추가. 신규 사용자 문구 없음(카탈로그 변경 없음).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 20:13:09 +09:00
songyc macbook
f70e6a1c06 fix(goal,watch,diary): judge goals as of their end date + watch l10n + template render polish
전체 재분석에서 확정한 4건 + 양식 개선 2건:

1) 목표 자동 판정 기준 시점 (핵심 수정)
   기존에는 판정이 "실행되는 시점의 현재 주기" 기준이라, 종료일
   며칠 뒤에 앱을 열면 목표 기간 밖의 데이터로 달성/미달성이
   갈렸다. 이제 자동 판정(evaluateIfEnded)은 **종료일이 속한
   논리적 하루의 마지막 순간**(judgmentReference) 기준 — 늦게
   실행해도, 종료일을 과거로 고쳐도 종료일까지의 기록으로 판정.
   (하루 시작 시간 설정으로 기준이 미래가 되는 경우 now로 상한)
   수동 종료는 종료일 없는 목표 전용이므로 기존대로 "지금" 기준.
   목표 편집 시 종료일 변경 처리: 진행 중 목표는 저장 즉시 판정,
   완료된 목표의 종료일을 바꾸면 '진행 중'으로 재개 후 재판정
   (미래/없음 → 진행 중 복귀, 다른 과거 날짜 → 그 시점 기준 재판정).
   검증: -endGoalYesterday로 어제 종료 → 종료일 기준 '달성' 판정 확인.

2) 워치 컴플리케이션 지역화 2건
   - 다짐 달성률 rectangular의 기간 라벨("하루/주간/월간")이 String
     파라미터라 미추출 → en/ja에서 한국어 노출 → String(localized:)
   - 빈 상태 문구("목표 없음"/"다짐 없음") 동일 문제 → 추출 + 워치
     앱/워치 위젯 카탈로그에 en/ja 번역 추가 (타깃별 stringsdata로
     sync — 카탈로그 오염 없음, 전 카탈로그 missing 0)

3) 배포 체크리스트: CloudKit 프로덕션 스키마 배포 항목을 CLAUDE.md
   §1에 추가 (DiaryTemplate 등 새 레코드 타입은 출시 전 대시보드
   Deploy Schema to Production 필수)

4) 일기 양식 렌더 개선
   - 첫 렌더 비동기화: 저장된 썸네일을 플레이스홀더로 즉시 깔고
     본 렌더(2×)를 백그라운드로 — 복잡한 벡터 PDF도 페이지 넘김이
     걸리지 않음
   - 비활성 페이지 고해상도 강등: 화면 밖 페이지는 래스터를 2×로
     상한(allowsHighResolution) — TabView가 이웃 페이지를 유지해도
     양식 여러 장의 메모리 사용을 절약, 활성 복귀 시 원래 배율 복원
   검증: 양식 페이지 2× 프로그램 줌에서 점 노트 선명(회귀 없음),
   시드 필기 페이지 획 유지 확인.

빌드: Debug·Store·워치 스킴 모두 성공. 도움말(목표 판정 문구
갱신 — 종료일 기준·재판정 규칙 안내)과 CLAUDE.md §4.3 갱신,
문구 수정 stale 1건 삭제 및 ko/en/ja 번역 완료.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 19:44:02 +09:00
songyc macbook
bdb97e3c9f feat(diary): imported page templates — PDF/photo backgrounds with per-page picker
일기 노트 페이지 양식(속지) 기능 (합의된 설계 그대로):

- 모델: DiaryTemplate 신설(원본 externalStorage — 여러 쪽 PDF도
  통째 1개만 저장 + pageCount) + DiaryPage.templateID/
  templatePageIndex(기본값 있는 새 필드, uuid 문자열 참조 —
  CloudKit 규칙·동기화 순서에 안전). 스키마에 등록.
- 양식 관리: 달력 툴바에서 진입 — PDF(파일, ≤20MB)·사진 가져오기,
  이름 변경·삭제(사용 중 페이지 수 경고 → 빈 캔버스 폴백, 필기 보존),
  첫 쪽 썸네일 저장. 쪽 상한 24쪽(초과분 안내).
- 페이지 추가: 양식이 있으면 [빈 캔버스/줄 노트/내 양식] 선택 시트,
  여러 쪽 PDF는 2단계 쪽 썸네일 선택(지연 생성). 양식이 없으면
  기존처럼 즉시 추가 — 흔한 경로의 반응성 유지.
- 렌더: 페이지 논리 좌표(768×1024)에 aspect-fit — 필기 좌표계 불변.
  PDF는 벡터 원본을 보존하고 줌 종료 시 현재 배율로 백그라운드
  재래스터(펜슬 캔버스 선명도 훅에 함께 연결 — 확대해도 흐림 없음).
  이미지는 가져올 때 긴 변 3072px 리샘플(필기 래스터 상한 3×와 동일).
  양식 페이지는 줄 노트 토글 숨김(상호 배타), 양식만 깔린 페이지도
  작성함 취급(hasContent). PencilKit 캔버스 코드는 건드리지 않음.
- 내보내기(PDF/PNG)에도 양식 배경 동일 반영.

검증(iPad 시뮬레이터, -diarySeedTemplate 등 신규 DEBUG 인자):
양식 관리·페이지 선택 시트·양식 페이지 렌더(2× 프로그램 줌에서
점 노트 선명) 스크린샷 + 이미지 내보내기 산출물에 양식 포함 확인.
Debug·Store 빌드 성공, 신규 문자열 22종 ko/en/ja(missing 0).
도움말 '나만의 양식(속지)' 항목 추가, CLAUDE.md §3·§6.7·§14 갱신.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 19:10:50 +09:00
songyc macbook
7a6a2aeb54 feat(diary): dashed calendar event blocks for visibility + per-date goal filter on the summary page
일기 요약 페이지 개선 2건:

1) 캘린더 일정 블록 가시성
   기존 옅은 채움(0.14)+실선 외곽선은 배경과 겹쳐 잘 안 보였다.
   구분 방향은 유지하되(꼬리표 색과 캘린더 색이 같아도 헷갈리지 않게)
   구분 수단을 '농도'에서 '스타일'로 변경 — 채움을 0.3으로 올려
   또렷하게 하고, 행동 블록(불투명 채움)과는 점선 테두리(1.5pt)로
   구분한다. 화면과 일기 내보내기(PDF/PNG)가 같은 뷰를 쓰므로 함께 반영.

2) '이 날의 목표' 필터
   카드 우상단에 타임테이블 필터와 같은 모양의 필터 버튼 추가 —
   그날 진행 중인 목표 중 보고 싶은 것만 선택(숨긴 개수 노란 배지).
   선택은 날짜별로 DiaryEntry.hiddenGoalIDs(uuid 문자열, 기본값 있는
   새 필드 — CloudKit 규칙 준수)에 저장되어 기기 간 동기화에 안전.
   시트는 전체 선택/해제 + 개별 체크(캘린더 일정 선택과 동일 패턴),
   열람 전용(프리미엄 만료)에서는 버튼 숨김, 빈 엔트리 정리 조건에도
   목표 필터 선택 보존 추가. 모두 숨기면 안내 문구 표시.

검증: Debug·Store 빌드 성공, iPad 시뮬레이터에서 점선 캘린더 블록·
필터 버튼·팝업(첫 목표 숨김 → 카드 제외 + 배지 1) 스크린샷 확인.
신규 문자열 ko/en/ja 번역(missing 0, 문구 수정 stale 1건 삭제).
DEBUG 인자 -diaryHideFirstGoal/-diaryShowGoalFilter 추가.
도움말(일정·필터 항목)과 CLAUDE.md(§3·§6.7·§14) 갱신.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 18:39:46 +09:00
songyc macbook
e652aff0e9 fix(review): tracking-type lock on edit, week-label l10n, premium feature list, catalog hygiene
전체 코드 최종 점검(보고)에서 지시받은 A·B·D 항목 처리:

A. 행동 수정 시 추적 방식 변경 잠금
   기존 행동을 수정할 때 추적 방식(시간↔횟수) 피커가 활성화되어
   있어 도움말("추적 방식은 바꿀 수 없으니")과 어긋났고, 바꾸면
   기존 기록이 화면·통계에서 보이지 않는 것처럼 되는 문제.
   → 수정 모드에서 피커 비활성 + "만들 때만 선택할 수 있어요"
   푸터 안내(ko/en/ja). 생성 시에는 기존과 동일.

B. 주차 라벨 지역화 2건
   - 통계 탭 월간 주차별 꺾은선 x축 "N주차"가 String(localized:)
     없이 하드코딩 — en/ja에서 한국어 노출 (내보내기와 불일치)
   - ⑤ 행동 통계 위젯 주 단위 라벨 "N주" — 같은 문제 + 앱과 표기
     불일치 → 둘 다 기존 번역 키 "%lld주차"(Week %lld/第%lld週) 사용

D. 결제 화면 기능 목록에 '제어 센터에서 실행' 항목 추가(ko/en/ja,
   도움말·CLAUDE.md와 일치) + Widgets/InfoPlist.xcstrings의
   CFBundleName을 IOS와 동일하게 shouldTranslate=false 처리.

C(시리 문구 미번역 의심)는 오탐 확인 — AppShortcuts 카탈로그는
stringSet(발화 변형) 구조로 ko/en/ja 모두 translated 상태.

검증: Debug·Store 빌드 성공, 영어 실행 통계 월간 화면에서
"Week 1~5" 표시 확인, 결제 화면에 제어 센터 항목 표시 확인,
전 카탈로그 missing 0·stale 0. CLAUDE.md §6.2 갱신.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 18:22:23 +09:00
songyc macbook
49cada45c7 feat(widget): add Now Tracking home widget — live timer with a stop button
새 홈 위젯 ⑥ '현재 진행 중'(HaruNowRunningWidget, 소형/중형/대형):

- 측정 중이 아니면 빈 상태 안내("지금 측정 중인 행동이 없어요")
- 측정이 시작되면 대표 행동(설정 '대표 시간 기준' — 다이나믹
  아일랜드와 같은 규칙)의 꼬리표 색 배경 위에 이름·시작 시각·
  실시간 타이머(Text(style:.timer))·종료 버튼을 표시
- 여러 개 측정 중이면 소형/중형은 "+N개 함께 추적 중", 대형은
  함께 측정 중인 행동을 줄로 나열하고 각각 종료 가능(4개 초과분은
  "외 N개"), 대표 하나뿐이면 오늘 누적(실시간)·시작 시각 카드
- 종료는 기존 RunActionIntent(시간형 토글) 재사용 — 짧은 기록
  무시 규칙·프리미엄 게이트·히트 영역(contentShape) 규칙 동일 적용
- 타임라인은 ①과 같은 예산 전략(주기 폴링 없음, 날짜 경계 안전망),
  탭 즉시 피드백(invalidatableContent), 틴트/클리어 렌더링 모드 대응
- 갤러리 미리보기는 측정 중이 아니어도 샘플로 모습 표시

검증: Debug·Store 빌드 성공, 앱 내 위젯 미리보기(-widgetPreview,
앵커 R/RL/RM/RE)로 소·중·대형/여러 개/빈 상태 스크린샷 확인.
l10n: 신규 문자열 ko/en/ja 번역(IOS·Widgets 카탈로그 missing 0,
stale 0 — 문구 수정으로 생긴 stale 1건 삭제, 잘못 stale로 남아
있던 위젯 전용 키 3건은 정상 상태로 복구). 도움말 위젯 항목과
CLAUDE.md §10에 ⑥ 추가.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 17:24:07 +09:00
songyc macbook
7720ed5925 fix(sync): receive CloudKit silent pushes so cross-device changes arrive while the app is open
다른 기기(아이폰↔아이패드)에서 측정을 시작/종료해도 사용 중인
기기에는 반영되지 않고, 모음 탭 새로고침 버튼을 눌러도 그대로이던
버그 수정. 앱을 나갔다 들어와야만 반영되던 증상의 원인.

원인: 푸시 엔타이틀먼트(aps-environment)는 있었지만
UIBackgroundModes에 remote-notification이 없어 CloudKit의
"데이터 바뀜" 무음 푸시가 앱에 전달되지 않았다. Core Data+CloudKit은
이 푸시로 가져오기(import)를 트리거하므로, 없으면 앱 실행/포그라운드
복귀 때만 가져온다. 새로고침 버튼은 로컬 스토어 재조회일 뿐이라
가져오기 전에는 옛 데이터만 보였다.

수정: iOS 타깃에 IOS/Info.plist 신설(UIBackgroundModes:
remote-notification)하고 INFOPLIST_FILE로 지정 — 생성 Info.plist
(GENERATE_INFOPLIST_FILE=YES)와 병합된다. IOS 동기화 그룹에
membershipException을 추가해 plist가 리소스로 중복 복사되지 않게 함.

검증: Debug·Store 빌드 성공, 두 빌드 산출물 Info.plist에
UIBackgroundModes=[remote-notification]과 기존 생성 키(표시 이름·
Live Activity) 병합 확인, 시뮬레이터 콜드 스타트 정상. 기기 간
동기화 도착은 실기기 2대에서 확인 필요.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 16:10:00 +09:00
songyc macbook
74dab79065 chore(l10n): normalize string catalog formatting (Xcode re-save, no content change)
Xcode가 카탈로그를 다시 저장하며 생긴 포맷 변화(콜론 앞 공백,
키 정렬 순서)만 반영. 키 추가/삭제 0, en/ja 번역 값 변경 0을
스크립트로 비교 확인함.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 16:09:41 +09:00
songyc macbook
9902d44848 feat(expiranner): rebuild as store-based expiry photo checker
- 매장 선택 → 연/월 → 월 캘린더(날짜별 제품 사진 썸네일) → 날짜 상세 플로우로 전면 재작성
- 스캔 플로우: 촬영 → 재촬영/사용 → 그 달 날짜만 선택 → 저장 후 카메라 자동 복귀 (손전등 지원)
- 날짜 상세 다중 선택 삭제 (전체 선택/해제 + 일괄 삭제 확인)
- 다크 전용 민트 테마, SwiftData 모델(Store ↔ ProductRecord, 원본+썸네일 분리 저장)
- 밝은 민트 앱 아이콘(SVG 원본 포함) + 런치 스크린/인앱 스플래시
- CLAUDE.md 새 구조 기준으로 재작성

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:53:14 +09:00
songyc macbook
6e0af0c33c fix(widget): make the whole interactive cell tappable, not just its visible content
인터랙티브 위젯(①행동 실행·③다짐 진행률)에서 셀의 일부 지점을
누르면 인텐트가 실행되지 않고 앱이 열리던 버그 수정.

원인: .plain 스타일 Button은 라벨의 투명한 영역(Spacer로 벌어진
공간, 여백, 링 안쪽)을 탭 판정에서 제외하는 경우가 있어, 해당
지점의 탭이 버튼에 잡히지 않고 위젯 기본 동작(앱 열기)으로 샜다.

수정: 두 위젯의 버튼 라벨 끝에 .contentShape(.rect)로 히트 영역을
셀 프레임 전체로 명시 — 아이콘·글자·프로그레스 링은 물론 셀 안
빈 공간까지 어디를 눌러도 인텐트가 실행된다. 시각적 변화 없음.
(③의 꼬리표 대상 다짐 셀은 표시 전용이라 기존대로 앱 열기)

검증: Debug·Store 빌드 성공, 위젯 미리보기 스크린샷으로 레이아웃
무영향 확인. CLAUDE.md §10에 히트 영역 규칙 추가.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 13:55:59 +09:00
songyc macbook
86bdf3d2d7 fix(sweep): full-app inspection fixes — watch command control reload, complication state key, bundle version, stale copy/catalog cleanup
전체 점검에서 발견한 저위험 문제들 일괄 수정:
- 워치 실행 명령 처리 경로에 ControlCenter.reloadAllControls() 추가
  (DataChange.commit과 동일 경로 유지 — 워치에서 시작/종료 시
  아이폰 제어 센터 토글이 낡은 상태로 남던 문제)
- 컴플리케이션 전용 전송 상태 키에 측정 시작 시각 포함
  (같은 행동을 종료 직후 디바운스 안에 재시작하면 상태 변화로
  인식하지 못해 컴플리케이션 타이머가 갱신되지 않던 엣지)
- 설정 탭 버전 표기를 하드코딩("1.0") 대신 번들
  CFBundleShortVersionString에서 읽도록 변경
- '대표 시간 기준' 설명의 낡은 문구 "(Live Activity 지원은 추후
  추가 예정)" 제거 — 실제 용처(다이나믹 아일랜드·잠금화면·워치
  컴플리케이션)로 갱신, en/ja 번역 포함
- 카탈로그 stale 항목 22개 정리 (IOS 20 + Widgets 2 — 과거 문구
  수정 때 남은 죽은 키), CLAUDE.md l10n 루틴에 stale 청소 단계 추가
- CLAUDE.md 남은 배포 작업에 코드 측 잔여 2건(약관 링크,
  ITSAppUsesNonExemptEncryption) 명시

검증: Debug·Store·워치 3스킴 빌드 성공, 전 카탈로그 missing 0,
인텐트 스모크 테스트 10개 통과, 스트릭 덤프 정상(시작일 하한 유지),
설정·모음 탭 스크린샷 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 13:42:54 +09:00
songyc macbook
8818578758 feat(widget,watch): instant tap feedback + stale complication timer guard
체감 개선 2건 (사용자 리포트 기반):

1) 홈 위젯 버튼의 '안 눌렸나?' 문제 — ①행동 실행·③다짐 진행률
   위젯의 변하는 값(누적·퍼센트·링)에 invalidatableContent() 적용.
   탭 즉시 해당 값이 '갱신 중' 표시로 바뀌어, 새 타임라인 도착
   (1~3초, WidgetKit 구조상 단축 불가) 전에도 탭 접수가 보인다
   → 중복 탭으로 횟수가 여러 번 올라가는 문제 방지.

2) 워치 컴플리케이션 유령 타이머 — 종료 푸시가 유실되면 '측정 중'
   타이머가 무한히 흐르던 문제. 스냅숏 generatedAt 기준 2시간
   (runningTrustInterval)을 넘긴 '측정 중'은 '새로고침 필요' 표시로
   강등. 워치 앱을 열면 즉시 복구되고 진짜 장시간 측정도 다음
   스냅숏에 타이머가 돌아온다 (데이터 무영향).

검증: 위젯 미리보기 레이아웃 무영향 확인, 워치 미리보기에 강등
샘플 행 추가(-complicationScrollTo stale)로 원형/사각형 렌더링 확인.
새 문자열 4개 워치 앱·워치 위젯 카탈로그 ko/en/ja 번역.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 13:01:05 +09:00
songyc macbook
430ec89341 fix(diary): stop pencil strokes vanishing when zooming the note page
아이패드 일기 노트에서 필기 후 핀치줌하면 획이 사라지고, 다시 그리는
동안엔 잠깐 보였다가 획을 떼면 또 사라지던 버그 수정.

원인: 줌 배율이 바뀔 때마다 SharpPencilCanvasView가 PencilKit 내부의
모든 서브레이어에 contentsScale 변경 + setNeedsDisplay()를 강제했는데,
PencilKit의 획 타일 레이어는 렌더러가 contents를 직접 채우는 방식이라
setNeedsDisplay()가 기존 픽셀만 지우고 다시 그려 주지 않음. 그리는
동안엔 라이브 렌더로 보이다가 canvasViewDrawingDidChange →
applySharpness에서 다시 지워짐.

수정: 배율 적용을 뷰 수준 contentScaleFactor 재귀로 한정하고 레이어
직접 조작·setNeedsDisplay 제거 — PencilKit이 자기 타이밍에 새 배율로
재렌더링해 획이 유지되면서 선명도(5916e7의 목적)도 유지된다.

검증(iPad 시뮬레이터): 시드에 필기 획 2줄 추가 + -diaryZoomScale
런치 인자(프로그램 줌) 신설 → 수정 전 2배 줌에서 획 소실 재현,
수정 후 동일 조건에서 획 유지·선명 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 03:12:42 +09:00
songyc macbook
973bb50cba feat(radial): double-tap the closed FAB to jump to a chosen tab
글래스 버블 메뉴가 닫힌 상태에서 FAB를 0.35초 안에 빠르게 두 번 누르면
설정한 탭으로 바로 이동하는 옵션. 첫 탭을 지연시키는 더블탭 제스처 인식은
메뉴 열림 반응성을 해치므로, 첫 탭은 즉시 메뉴를 열고 두 번째 탭에서만
분기한다 — 더블탭 시 버블이 피어나다 FAB로 흡수되며 화면이 바뀌는 연출.

- 설정(내비게이션, 버블 모드 전용): '두 번 눌러 바로 이동' 토글 +
  '이동할 탭' 피커 + 푸터 설명. settings.radialDoubleTapTab
  (rawValue, 빈 값=꺼짐, 기기 로컬)
- 검증용 런치 인자 -radialDoubleTapTest (2초 뒤 더블탭 재현) —
  통계 탭 이동 스크린샷으로 확인
- 도움말: 버블 메뉴 항목에 더블탭 문장 추가 + 누락돼 있던
  '홈 화면·잠금화면 위젯' 사용법 항목 신설
- l10n 6개 문자열 ko/en/ja, CLAUDE.md §6·§6.8·§14 갱신

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 02:54:14 +09:00
songyc macbook
24c3004ead feat(widget): show span accumulated value in quest ring widget
③ 다짐 진행률 위젯에서 '이하 유지' 다짐은 시작부터 링이 가득 차 있고
문구도 '오늘 한도 지킴'뿐이라 지금 몇 회/몇 시간 했는지 알 수 없던 문제.
퍼센트(또는 한도 지킴/초과) 문구와 연속 달성 문구 사이에 위젯 옵션의
진행률 기간 기준 실제 누적값 한 줄을 추가한다 ("1시간 20분" / "3회").
'이상 달성' 다짐에도 동일하게 표시. 주간·월간 진행률 캡과 무관한
원본 누적값이며, 다른 위젯(②④)과 나머지 레이아웃은 그대로.

- QuestCellSnapshot.valueLabel 추가 (spanRange 원본 값 포맷)
- QuestProgress.spanRange(_:now:) 헬퍼 추출
- 위젯 미리보기에 '이하 유지(소형)' 행 신설 (앵커 CM)
- l10n: '0분' ko/en/ja (IOS·Widgets 카탈로그)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 02:37:41 +09:00
songyc macbook
379ea6ebf4 fix(quest): bound streaks by the goal's start date
'이하 유지' 다짐은 기록 없는 날도 달성으로 집계되므로 연속 계산이
목표가 생기기 전 과거(스캔 상한 400일)까지 거슬러 올라가 '연속 401일'로
표시되던 버그 수정. 연속은 행동이 아니라 '목표 안 다짐'의 달성이므로
하한 = 목표 시작일:
- 하루 단위: 시작일이 속한 논리적 하루부터만 센다
- 주간/월간: 시작일이 걸친 부분 주기까지 포함, 그 이전 주기에서 중단

검증: 시드에 재현 목표(스크린 타임 관리, 3일 전 시작 + 초과 없는 이하
유지 다짐) 추가 → 기대값 연속 4일 확인. -streakDump 런치 인자 신설
(전 다짐의 연속 계산 결과를 Documents/streak-dump.txt로 덤프).

부수: 도움말에 '연속 달성 표시' 항목 추가(ko/en/ja). 지난 기능들의
문자열이 위젯·워치·InfoPlist 카탈로그에 미번역으로 남아 있던 것을
채움(제어 센터·연속 표기·즐겨찾기·캘린더 권한 문구) — l10n 루틴이
IOS 카탈로그만 확인하던 함정을 CLAUDE.md §2.3에 명시.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 01:42:13 +09:00
songyc macbook
ce8ca50feb docs(help): update help & CLAUDE.md for the new feature batch, l10n fill
- 도움말: 장시간 측정 알림(측정 토픽), CSV 백업(새 토픽), 일기 텍스트
  상자·페이지 순서, 워치 즐겨찾기, 제어 센터 실행(새 토픽), 만료 후
  데이터·일기 정책 문구 반영
- CLAUDE.md: §3 DiaryPageItem.text, §5.3 커밋 전파(컨트롤 리로드·워치
  디바운스·알림 동기화), §6.1 온보딩 빈 화면, §6.7 만료 열람 모드·텍스트
  상자·페이지 순서, §6.8 장시간 알림·데이터 내보내기, §8 만료 정책,
  §9 워치 즐겨찾기, §10 제어 센터 컨트롤, §12 컨트롤 인텐트, §14 새 인자
- 신규 문자열 49건 en/ja 번역 (missing: [])
- 최종 검증: Debug·Store·watchOS 3개 스킴 빌드 성공, 인텐트 스모크
  테스트 10개 시나리오 재통과

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 00:24:29 +09:00
songyc macbook
e4bba280ad feat(control): Control Center toggle for running an action (iOS 18 control)
제어 센터/잠금화면 하단/액션 버튼에 배치할 수 있는 '행동 실행' 컨트롤:
- 행동 하나를 골라(미선택 시 배치 첫 행동) 시간형은 시작/종료 토글,
  횟수형은 탭마다 +1 (토글 표시는 꺼짐으로 복귀)
- 전용 ToggleActionControlIntent(SetValueIntent + LiveActivityIntent) —
  앱 프로세스 실행이라 제어 센터에서 시작해도 다이나믹 아일랜드가 바로 뜸.
  단축어 앱에는 숨김(isDiscoverable=false), 프리미엄 게이트 적용
- DataChange.commit / IntentStore.performWrite에
  ControlCenter.reloadAllControls() 추가 — 앱/위젯/시리/워치 어느 경로로
  측정 상태가 바뀌어도 컨트롤 토글 표시가 따라온다

시뮬레이터에는 제어 센터에 컨트롤을 추가할 수단이 없어 빌드·인텐트
메타데이터 등록 확인까지 검증 (실기기에서 배치·토글 확인 필요).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 00:19:36 +09:00
songyc macbook
f22aea0f95 feat(onboarding): actionable empty state on the main tab
첫 실행의 빈 모음 탭이 안내 문구뿐이던 것을 개선:
- 도메인 개념 한 줄 소개 + '첫 행동 만들기'(행동 추가 시트 직행)
- '예시 행동으로 시작해 보기' — 즉시 써 볼 수 있는 예시 세트
  (꼬리표 생활·건강 + 독서/운동/물 마시기) 생성. 행동 추가와 같은
  경로(LocalPrefs 배치 등록 + DataChange.commit)를 쓰며 언제든 수정·삭제 가능

깨끗한 설치 상태 스크린샷으로 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 00:17:42 +09:00
songyc macbook
d7b602f08b feat(export): raw data export to CSV from Settings
설정 → 데이터 → '데이터 내보내기': 행동·시간 기록·횟수 기록·목표·다짐을
CSV 5개 파일로 만들어 공유한다 (백업·이동·스프레드시트 분석용).
- 가공 없는 원본 값, 시각은 ISO 8601, 필드 이스케이프 처리,
  엑셀 한글 인식용 UTF-8 BOM
- 무료 사용자도 사용 가능 (iCloud 백업이 없는 무료 사용자의 백업 수단)
- 검증용 -dataExportPreview(화면 직행)·-dataExportRun(자동 생성 후
  Documents 복사) 추가. 시뮬레이터에서 5개 파일 생성·내용 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 00:16:10 +09:00
songyc macbook
64c9f2d619 feat(diary): text boxes, page reorder, read-only browsing after expiry
- 텍스트 상자 요소: 노트 페이지 툴바에 '텍스트' 추가 — 키보드로 쓰는
  글 상자를 배치 요소로 올린다 (배치 모드에서 이동·크기 조절, 선택 시
  '글 수정'으로 내용·색 편집, 내보내기에도 그대로 렌더).
  DiaryPageItem에 text 필드 추가(kind .text, CloudKit 기본값 규칙 준수)
- 페이지 순서 변경: ⋯ 메뉴 → '페이지 순서 변경' 시트에서 노트 페이지를
  드래그로 재배열 (요약 페이지는 항상 첫 장)
- 프리미엄 만료 후 열람 허용: 이미 쓴 일기가 있으면 잠금 화면 대신
  달력을 열람 전용으로 연다 — 안내 배너 + 일기 있는 날짜만 활성,
  진입만으로 엔트리를 만들지 않고, 기분/할일/필기/필터/캘린더 등
  모든 편집 UI 비활성 (environment diaryReadOnly로 전파).
  일기를 쓴 적 없는 미구매 사용자는 기존 잠금 안내 유지

검증(iPad): 시드된 노트 페이지에서 텍스트 상자 렌더·툴바 확인,
-premium NO에서 배너/날짜 비활성/편집 UI 숨김 스크린샷 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 00:13:11 +09:00
songyc macbook
59d6105d21 feat(tracking): long-running session alerts, timer cap fix, memo save
- 장시간 측정 알림: 측정이 설정한 시간(1/2/3/6/12시간, 기본 끔)을 넘기면
  로컬 알림으로 종료를 잊었음을 알려준다. LiveActivityManager.sync 길목에서
  진행 세션과 예약을 동기화하므로 앱·위젯·시리·워치 어느 경로로
  시작/종료/수정해도 예약이 따라가고, 세션이 끝나거나 설정이 바뀌면
  기존 예약이 제거된다 (식별자에 행동·시작시각·설정시간 포함).
  설정 탭 '측정' 섹션에서 켜는 순간 권한 요청.
- Live Activity 타이머 상한 30일 → 1년 (상한 도달로 타이머가 멈춰 보이던
  엣지 제거)
- 메모 시트(측정 종료/횟수 기록) 저장 시 명시적 context.save — autosave
  의존 제거
- 검증용 -longSessionAlertTestSeconds(시간 대신 N초 발화)·-alertDump
  (예약 상태를 Documents/session-alerts.txt로) 추가. 시뮬레이터에서
  예약 생성(진행 세션 2건)과 설정 해제 시 제거를 덤프로 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 00:04:08 +09:00
songyc macbook
b980d7df7a feat(watch): favorites list on the first screen, debounced snapshot push
- 워치 첫 화면 최상단에 '즐겨찾기' 항목 추가 — 앱에서 즐겨찾기한 행동을
  태그를 거치지 않고 한 탭 만에 연다. WatchActionInfo.isFavorite는
  optional로 실어 구버전 캐시 스냅숏도 그대로 디코딩된다.
- 워치 스냅숏 전송 디바운스(350ms): 모든 커밋마다 목표×다짐×3기간
  진행률을 전부 재집계하던 것을, 연타 중에는 마지막 상태 한 번만
  계산해 보내도록 변경 (명령 응답 경로의 즉시 스냅숏은 그대로).

iOS·watchOS 스킴 빌드 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 23:56:32 +09:00
songyc macbook
42b95c5d7f feat(quest): consecutive-achievement display on quests, plus wording fixes
다짐 단위 '연속 달성' 표기 추가 (QuestProgress.streak → QuestStreakInfo):
- 하루 단위 다짐: 적용일만 세는 연속 계산 — 매일이면 연속 일수,
  '매주 수요일'이면 수요일들만 이어서 센다 (4주 연속 = 연속 4일)
- 주간/월간 다짐: 주기 전체 달성 여부로 연속 N주/N달
- 특정 기간 다짐: 반복이 없어 표시하지 않음
- 진행 중인 오늘/이번 주기는 '이상 달성'이면 미달이어도 끊기지 않고
  건너뛰며, 채웠으면 포함. '이하 유지'는 한도를 넘는 순간 끊김
- 성능: 기록을 논리적 하루 키로 1회 버킷팅해 O(기록수+일수),
  소급 상한 400일

표기(기존 디자인·데이터는 그대로 두고 추가만, 0이면 숨김):
- 목표 탭 다짐 행: 이름 옆 불꽃 + "연속 N일" 배지 (노랑)
- 위젯 ③ 다짐 진행률: 퍼센트 아래 한 줄
- 위젯 ④ 다짐 현황: 이름 아래 작은 글씨
- 위젯 ② 목표+다짐 구성: 퍼센트 아래 6.5pt — 연속 표기가 있을 때만
  행 높이 14→19 (적응 채움이 자동 반영)
- 문구는 '스트릭' 대신 "연속 N일/주/달" (en은 streak, ja는 N日連続)

문구 정정: '몇째 주 요일' → '몇 번째 요일' — 구현(weekdayOrdinal)은
달력의 주차가 아니라 "그 달의 n번째 ◯요일"이므로 편집 화면 픽커와
주기 요약 문구를 실제 동작에 맞게 수정 (동작 변경 없음). en/ja 번역 포함.

검증: 시드 데이터로 목표 탭 배지 손계산 대조(독서 연속 3일 — 3일 전
기록 없음 / 영상 시청 어제 한도 초과로 연속 1일 등 전부 일치),
위젯 ②③④ 미리보기 스크린샷으로 레이아웃 넘침 없음 확인,
missing: [], Debug·Store 스킴 빌드 성공.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 23:49:45 +09:00
songyc macbook
076dad26d8 feat(widget): adopt LiveActivityIntent on the widget run button too
RunActionIntent(위젯 실행 버튼)도 LiveActivityIntent를 채택 — 시스템이
앱 프로세스에서 실행해, 위젯 탭으로 시간 측정을 시작해도 시리 경로와
동일하게 다이나믹 아일랜드가 바로 뜬다.

과거 '위젯 버튼 먹통' 증상 때문에 확장 프로세스 고정을 유지했었으나,
해당 증상은 위젯 재추가·기기 재부팅(캐시)으로 해소된 것으로 확인되어
제약을 풀었다. 실기기에서 응답성 회귀가 보이면 이 인텐트의
LiveActivityIntent 채택 한 줄만 되돌리면 된다 (코드 주석에도 기록).

검증: -intentSmokeTest에 위젯 실행 토글 시나리오 추가(시작 → 토글 종료),
10개 시나리오 전부 통과. Debug·Store 스킴 빌드 성공.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 21:37:42 +09:00
songyc macbook
ec62dc83de feat(siri): background Live Activity, spoken action names, localized phrases
시리 단축어 점검에서 나온 추천 수정 일괄 적용:

- StartTimeActionIntent·StopTimeActionIntent가 LiveActivityIntent 채택 —
  시리/단축어의 백그라운드 실행에서도 Live Activity 시작이 허용되어
  앱을 열지 않아도 다이나믹 아일랜드에 측정이 표시된다.
  RunActionIntent(위젯 버튼)는 확장 프로세스 응답성 튜닝을 유지하기 위해
  의도적으로 제외.
- 행동 이름 파라미터 문구 추가: "시리야, 하루 다님에서 독서 측정 시작"처럼
  되묻지 않고 한 번에 실행. 어휘 갱신을 위해 앱 시작 시
  updateAppShortcutParameters() 호출 (새 행동은 다음 실행부터 인식).
- IOS/AppShortcuts.xcstrings 신설 — 시리 문구 ko/en/ja 지역화.
  빌드 로그의 --no-app-shortcuts-localization가 사라지고 root.ssu.yaml에
  en/ja 발화가 포함되는 것 확인.
- RunActionIntent isDiscoverable=false — 생 UUID 파라미터라 사용자가 조합할
  수 없는 위젯 전용 동작을 단축어 앱·스포트라이트에서 숨김 (위젯 버튼은
  그대로 동작).
- 결제 화면 기능 목록에 '시리 단축어' 추가. 이 과정에서 featureRow가
  String 파라미터라 기능 목록 6줄 전체가 미지역화였던 버그 발견 —
  LocalizedStringKey로 바꾸고 en/ja 12개 문자열 번역.

검증: -intentSmokeTest 9개 시나리오 전부 통과(LiveActivityIntent 전환 후
재확인), 프리미엄 화면 렌더링 확인, Debug·Store 스킴 빌드 성공.
남은 확인: 시리 음성 인식·백그라운드 Live Activity 실표시는 실기기 필요.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 21:08:48 +09:00
songyc macbook
ecfd008229 docs(siri): add Shortcuts help topic and an in-app intent smoke test
시리 단축어 기능 점검:
- 도움말(프리미엄 그룹)에 '시리·단축어로 기록하기' 항목 추가 — 그동안
  도움말에 시리 단축어 안내가 없었다. en/ja 번역 포함
- DEBUG 검증용 -intentSmokeTest 런치 인자 추가: 인텐트 6종의 perform()을
  앱 안에서 9개 시나리오(시작/중복 방지/종료/횟수 추가/타입 방어/조회
  3종/프리미엄 게이트)로 실행하고 결과를 Documents/intent-smoke-test.txt에
  남긴다. 시뮬레이터 실행 결과 9개 전부 통과
- CLAUDE.md §12·§14에 검증 방법과 현재 한계(AppShortcuts 지역화 미구성,
  '선택 안 함' 센티널의 단축어 앱 노출) 기록

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 20:51:30 +09:00
songyc macbook
1b36607ba1 feat(radial): rebuild glass bubble menu on native Liquid Glass
글래스 버블 메뉴를 시스템 리퀴드 글라스(glassEffect) 기반으로 재구축.
컨셉(FAB → 부채꼴 유리 구슬, 엄지 반경 ~150pt, 바깥 탭 닫기, 순서 설정)은
그대로 두고 유리 질감·연출·성능을 올렸다.

유리: material 흉내(ultraThinMaterial + 그라데이션 + 테두리) 대신
.glassEffect(.regular.interactive()) — 배경 굴절·빛 반사·눌림 시머까지
시스템이 그린다. 현재 탭은 초록 틴트 유리.

연출: 2단계 — 삽입 순간 FAB 유리에서 모핑으로 부풀어 나온 뒤(bloomed)
스태거 바운스 스프링으로 아치에 흩어진다. GlassEffectContainer 근접
블렌딩으로 FAB를 벗어날 때 유리가 늘어나다 물방울처럼 분리. 닫힘은
지연 없이 흡수. 그라데이션 딤 + 메뉴 뒤 초록 빛 무리 + 햅틱.

성능(버벅임): 예전엔 블러 14장(버블 7 + 라벨 알약 7) + 그림자 14개가
각각 애니메이션됐다. 라벨을 구슬 안에 넣어 유리 요소를 8개로 줄이고
GlassEffectContainer가 한 패스로 일괄 렌더링, 커스텀 그림자 제거.

주의점 기록: 컨테이너 안 유리 요소는 glassEffect 뒤 opacity/scale이
콘텐츠에 온전히 적용되지 않아(접힌 상태 내용물 비침) 숨김은 계층
제거(if expanded), 이동은 offset으로만 처리.

검증: 접힘·펼침(라이트/다크) 스크린샷으로 유리 굴절·라벨 가독성·
콘텐츠 누출 없음 확인. Debug·Store 스킴 빌드 성공.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 20:16:25 +09:00
songyc macbook
a9473be937 fix(diary): clean up empty entries created by merely viewing a day
일기 상세는 진입만 해도 DiaryEntry를 생성(loadEntry)해서, 열람만 한
날짜의 빈 엔트리가 스토어와 CloudKit에 계속 쌓였다. 달력 화면
onAppear에서 완전히 빈 엔트리만 정리한다 (상세에서 달력으로 돌아올
때마다 실행되고, 이 수정 이전에 쌓인 빈 엔트리도 함께 청소됨).

보존 조건을 엄격히 판정:
- hasContent(기분·할 일·내용 있는 페이지)는 물론
- 달력 마커에 안 잡히는 캘린더 일정(calendarEventIDs)·타임테이블
  필터(hiddenActionIDs) 선택이 있으면 보존
- 아직 내용 없는 노트 페이지를 만들어 둔 날도 보존 (페이지 0개일 때만 삭제)

검증(iPad 시뮬레이터, 스토어 직접 조회):
- 열람만 하고 나간 빈 엔트리 → 달력 재진입 시 삭제 (1건 → 0건)
- 타임테이블 필터만 설정한 엔트리 → 정리 후에도 보존, 재진입 시
  필터 배지(1)와 숨김 상태 유지 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 19:40:46 +09:00
songyc macbook
0aed7a9ec5 fix(goal): evaluate ended goals at launch and foreground, not only on goal tab
종료일이 지난 목표의 자동 판정(evaluateIfEnded)이 목표 탭 진입 시에만
실행돼, 목표 탭을 열지 않으면 위젯·워치·모음 탭 카드에 '진행 중'으로
계속 남아 있었다. 앱 실행/포그라운드 복귀(scenePhase .active) 시점에
같은 판정을 추가로 실행한다.

- 목표 탭 onAppear 로직과 동일한 코드 — 진행 중 목표만 조회, 변경이
  있을 때만 DataChange.commit (위젯·워치·라이브 액티비티 갱신)
- 다짐 없는 목표는 evaluateIfEnded가 건드리지 않으므로 '확인 필요'
  직접 선택 흐름은 그대로
- 위젯 확장에서는 판정하지 않음 (프로바이더 경합·CloudKit 내보내기
  지연 위험 회피)
- 검증용 DEBUG 인자 -endGoalYesterday "제목" 추가. 시뮬레이터에서
  목표 탭을 열지 않은 콜드 스타트만으로 statusRaw가 achieved로
  전환되는 것을 스토어 조회로 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 19:37:43 +09:00