Skip to content

Screen-time integration branch: iOS calendar UX, OAuth resilience, evening review - #50

Open
oyeong011 wants to merge 18 commits into
developfrom
feature/screen-time-integration
Open

Screen-time integration branch: iOS calendar UX, OAuth resilience, evening review#50
oyeong011 wants to merge 18 commits into
developfrom
feature/screen-time-integration

Conversation

@oyeong011

Copy link
Copy Markdown
Owner

Summary

feature/screen-time-integration 브랜치 누적 작업 15 commit 을 develop으로 머지. 크게 다음 영역:

iOS

  • Calendar UX 대규모 개편: 드래그·롱프레스로 이벤트 이동/리사이즈, 완료 토글, DayDetail/EventDetail/Review 탭 (157270c)
  • Google OAuth refresh 안정화: 일시 401/403에 토큰 보존 (45b53ff)
  • Google Calendar delete 자동 재시도 + 한국어 LocalizedError (c8435e9)

macOS (Calen)

  • 저녁 리뷰 알림 → 팝오버 자동 오픈 + 자동적용 opt-in 토글 (97fccbf, 0933f00)
  • AIService CLI 경로 lazy resolve + PATH allowlist 보강 (f7e3232)
  • 메뉴바 아이콘 tint 복원, 동물 스프라이트 확장 (14dbca9, 8ba7ed8, 8ab6fb5)

Shared

  • CalendarEvent.isCompleted + Google extendedProperties.calenCompleted 양방향 sync
  • CalendarUX/CalendarInteractionMath 추출 (iOS 드래그 / macOS evening reschedule 공유)

Docs / Tooling

  • 랜딩 페이지 카피·헤로 mockup 개편 (6fd4a58)
  • OAuth 문제 진단 가이드 (docs/oauth-troubleshooting.md)
  • CLI 재인증 도우미 스크립트 (scripts/reauth-cli.sh)
  • v0.4.67 / v0.4.68 릴리즈 metadata

Test plan

  • swift test — 357 tests / 16 suites 통과 (Tests/CalendarUXRegressionTests, GoogleCalendarClientTests 신규 포함)
  • iOS Xcode 빌드 후 실기기에서 캘린더 드래그·리사이즈 동작 확인
  • iOS에서 의도적 OAuth 401 (네트워크 차단 등) 발생 시 토큰 보존 확인
  • macOS Calen 저녁 리뷰 알림 탭 → 팝오버 + 리뷰 패널 전환 확인
  • 랜딩 페이지 GitHub Pages 배포 확인 (https://oyeong011.github.io/Planit/)

🤖 Generated with Claude Code

oyeong011 and others added 15 commits May 3, 2026 21:29
- deleteEvent now swallows 404 as idempotent success, retries without
  If-Match on 412 (etag stale), and refreshes + retries on 401.
- Adds LocalizedError conformance with Korean user-facing messages so
  iOS no longer surfaces raw "Could not delete google calendar." text.
- Regression tests cover the three new recovery paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Tap on the evening review / midnight rollover notification opens the
  popover (new AppDelegate handler) and routes MainView straight to the
  review panel via NotificationCenter signal.
- runAutomaticEveningReviewIfNeeded no longer moves todos behind the
  user's back. Auto-apply only runs when the new
  profile.eveningReviewAutoApply flag is explicitly enabled in Settings.
- SettingsView grows an "auto-reschedule unfinished items" toggle under
  Evening Review, with KO/EN strings.
- Pulls in the in-progress EveningReviewRescheduler + ReviewService
  scheduling helpers that this flow depends on.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scripts/reauth-cli.sh probes codex + claude CLI auth state, with
  --fix for interactive re-login and --codex-api-key to swap a stuck
  ChatGPT-account codex session over to API-key mode (the failure mode
  that blocked our last delegated codex run).
- docs/oauth-troubleshooting.md walks through the four common reasons
  the macOS Google sign-in flow ends in "cancel" (consent screen still
  in Testing, wrong OAuth client type, missing bundled credentials,
  90s loopback timeout).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
기존: refresh 응답이 HTTP 401/403 이면 무조건 logout() — rate limit,
일시 5xx, 클럭 스큐 같은 일시 오류에도 키체인 토큰이 날아가서 사용자
가 매번 재로그인하게 됨.

수정:
- JSON 본문 먼저 파싱 → error="invalid_grant" / unauthorized_client
  / invalid_client 일 때만 logout() 실행
- 비-200 응답에 error 키 없으면 토큰 보존, 다음 호출 때 재시도

scope 축소도 함께: 풀 `calendar` (restricted, CASA Tier 2 평가 필수)
→ sensitive 등급 `calendar.events` + `calendar.calendarlist.readonly`
+ `userinfo.email` 조합. Calen은 캘린더 자체 생성/삭제/공유 안 하므로
충분.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Lazy resolve: chat 호출 시점에 선택된 provider CLI 경로만 재탐색
  (앱 시작 시 양쪽 다 resolve하던 비용 제거)
- PATH allowlist: 환경 PATH에서 찾은 경로도 /opt/homebrew, /usr/local,
  /usr/bin, /bin, $HOME 하위만 허용 (임의 디렉터리 실행 방지)
- CLI 실행 환경 PATH 확장: nvm/asdf/volta/bun/cargo/npm-global 등 사용자
  설치 경로 포함해 claude/codex 의 노드 의존성 해소율 향상

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shared:
- CalendarEvent.isCompleted 필드 추가 (Codable 마이그레이션 포함).
  Google Calendar 양방향 sync는 extendedProperties.private.calenCompleted
  마커로 처리.
- CalendarUX/CalendarInteractionMath: 이벤트 드래그/리사이즈 시 시작-종료
  시간 계산 / 다른 날로 시프트 / 그리드 좌표 ↔ 시간 변환을 공유 로직으로
  추출 (iOS HomeView/MonthGridView, macOS EveningReviewRescheduler가 함께
  사용).

iOS (CaleniOS):
- HomeView/MonthGridView/WeekExpansionView: 주/월 그리드에서 드래그·롱
  프레스로 이벤트 이동·리사이즈, 빈 슬롯 탭으로 즉시 일정 생성, 완료
  토글 인터랙션 통합.
- DayDetailSheet/EventDetailSheet: 완료 상태 표시·토글, 리사이즈 핸들,
  반복 일정 메타데이터 표시 강화.
- ReviewTabView: 일간/주간 리뷰 진입점 + 평가 입력 UI.
- ReviewViewModel/HomeViewModel: 캘린더 인터랙션 상태(in-flight 드래그
  대상, 시프트 미리보기) 관리.
- Schedule/Color/Theme/FakeEventRepository: 새 인터랙션·완료 상태에
  필요한 모델·테마 보강.

Tests:
- CalenTests: automaticEveningReschedule 트리거 조건 / 완료 마커 직렬화
  / CalendarInteractionMath 검증.
- CalendarUXRegressionTests (신규): 그리드 좌표 변환, 다른 날 시프트,
  드래그 리사이즈 경계 케이스 회귀 방지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- docs/index.html: 카피·섹션 재정렬, 헤로 영역에 제품 mockup 이미지 사용
- docs/calen-product-mock.png 추가: 새 헤로용 시각 자료

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6fd4a5853b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +227 to +232
repo.replaceInMemory(updated)
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
Task { @MainActor in
do {
let saved = try await repo.update(updated)
repo.replaceInMemory(saved)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the home calendar in sync after sheet edits

When an event is toggled, dragged, or resized from the day sheet, this path updates only repo.events; the month grid and week expansion are driven by HomeViewModel.schedulesInMonth, and there is no subscription or refresh after this sheet mutates the repository. In the Google-backed flow, dismissing the sheet therefore leaves the main calendar showing the old time/completion state until a later refetch/month navigation. Route these edits through the view model or notify the parent to patch/refresh schedulesInMonth after the save/rollback.

Useful? React with 👍 / 👎.

/// 기본 seed=false. 과거에 true가 기본이었는데, 사용자가 빈 셀을 눌러도 오늘 기준
/// +1/+2일 등의 가짜 시드 이벤트("오프사이트 워크숍" 등)가 시트에 표시되는 혼란이 있었음.
/// Preview/QA에서 데모 데이터가 필요하면 명시적으로 `FakeEventRepository(seed: true)`로 호출.
public init(seed: Bool = false) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve local day details when Google is disconnected

When HomeView is not using Google it still passes HomeViewModel.eventRepository to DayDetailSheet, but that repository is now constructed as FakeEventRepository() and this default leaves events empty; nothing mirrors the SwiftData Schedule rows that populate schedulesInMonth into this repo. In the logged-out/local mode, tapping a date therefore opens an empty day sheet even when the month grid contains local schedules. Either feed the local schedules into the sheet or keep the repository populated for the non-Google path.

Useful? React with 👍 / 👎.

oyeong011 and others added 3 commits May 27, 2026 18:33
Hardened Runtime requires loaded frameworks to share the main binary's
Team ID. Sparkle artifact ships ad-hoc signed without --options runtime,
so combining it with `codesign --options runtime --sign -` on the main
bundle causes dyld to reject the framework with "different Team IDs"
(SIGKILL at launch, same class as v0.4.5 regressions).

Solve: when DEVELOPER_ID is absent, re-sign Sparkle (XPC services →
Autoupdate → Updater.app → framework) with the same `--options runtime
--sign -` combo so both ends are ad-hoc + hardened. Sparkle auto-update
won't work in this mode (acceptable: this path is for local test/internal
distribution only — production builds set DEVELOPER_ID and follow the
normal Developer ID code path above).

Detected while smoke-testing 0.4.69 ad-hoc build locally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
syncHistory()가 364일 × 모든 캘린더 이벤트를 파싱하면서, 이벤트마다
parseGoogleDateTime / parseGoogleAllDayDate가 ISO8601DateFormatter
/DateFormatter 를 새로 alloc 했다. ICU init 비용으로 메인 스레드가
sustained 44~89% CPU 로 수십 초간 점유되는 핫스팟이었음
(v0.4.69 sample.txt 확인 — 거의 모든 시간이 parseGoogleDateTime →
NSISO8601DateFormatter init 에서 소비).

정적 인스턴스 + NSLock 으로 재사용해 alloc 비용 제거.
포매터들은 stateless하게 사용되므로 락 구간 짧음.

측정 결과 (v0.4.69 → v0.4.70 로컬 빌드):
- idle CPU 44~89% → 0.0%
- RSS 187 MB → 63 MB

build(macos): drop --options runtime in ad-hoc signing path

Hardened Runtime + ad-hoc Sparkle 조합은 library validation 이 team ID
매칭을 강요해서 dyld가 Sparkle 로드를 거부 (v0.4.69 ad-hoc 빌드 launch
실패). 로컬/내부 배포용 ad-hoc 빌드는 runtime 옵션을 빼고 --deep ad-hoc
로 일원 서명. production (DEVELOPER_ID 있음) 분기는 그대로 hardened
runtime 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
"오늘 일정 알려줘" 같은 질문에 AI가 "0개" 라고 답하던 버그.
ChatView.sendMessage()가 viewModel.calendarEvents / categories 는
AIService에 주입했지만 viewModel.todos 는 주입하지 않았다.
buildCalendarContext()도 캘린더 이벤트만 인지하고 todos 는 모르므로
사용자가 우측 패널에서 보고 있는 할일(perf실습, 로봇플젝, 등)이
컨텍스트에 전혀 없는 상태에서 답을 만들었음.

수정:
- AIService.cachedTodos 필드 추가
- ChatView.sendMessage()가 viewModel.todos 도 주입
- buildCalendarContext()가 오늘~14일 todos 를 "=== 향후 2주 할일 ===" 섹션으로
  컨텍스트 말미에 첨부 (캘린더 이벤트와 분리 표시, 완료 체크 마커 포함)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant