Before adding "every N weeks/months" support, found the recurrence
window math was independently hand-written in three places (main app's
TaskItem.isOccurrence, and two separate widget extensions' countdown
progress calculations) with no shared code to keep them in sync.
Extracted the math into Shared/RecurrenceRule.swift (pure Foundation,
no framework deps) compiled into both the app and widget extension
targets via a new project.yml Shared/ source path, and pointed all
three call sites at it. Verified every case is identical to prior
behavior at interval==1 by compiling and running a standalone test
driver directly against the real file with swiftc - 38/38 passing,
including Jan-31-monthly and Feb-29-leap-year-yearly edge cases.
Groundwork only - no recurrenceInterval field on TaskItem yet, the
actual interval feature comes next on top of this.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The mic icon in AddTaskSheet was a static Image with no action and no
speech service behind it. Full implementation:
SpeechRecognizer.swift (new):
- @MainActor ObservableObject wrapping SFSpeechRecognizer + AVAudioEngine
- Requests speech recognition then microphone permission in sequence
- Streams partial results into @Published transcript; final result stops engine
- Suppresses transient error 301 (no speech detected)
- reset() cleans up on sheet dismiss
- [VOICE] log trail at every step: tapped → granted → started → transcript → saved
TodayView.swift — AddTaskSheet:
- @StateObject private var speech = SpeechRecognizer()
- onChange(speech.transcript) writes transcript to rawText (task field)
- Mic Image → Button { speech.toggle() } with mic.fill + pulse animation while recording
- .safeAreaInset shows a red dismissible error banner on permission denial
- submit() stops recording before saving; logs task title
- .onDisappear resets speech state
project.yml:
- NSMicrophoneUsageDescription
- NSSpeechRecognitionUsageDescription
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
Root cause: a data-integrity bug, not visual. The month grid is built from
.weekOfMonth (respects Calendar.current.firstWeekday), but the weekday header
was hardcoded Sunday-first ("S M T W T F S") and the week-number label was gated
on isSunday (weekday == 1). On any non-Sunday-first locale (or when the user sets
"Start Week On" = Monday) every column was mislabeled by one and the W-number
landed on the wrong column — e.g. June 1 2026 (a Monday) appearing under "S".
Fix:
- Extract pure, testable date math into CalendarGrid (monthGrid + weekdaySymbols).
- Header now derived from firstWeekday, so it always matches the grid.
- Week-number label gated on isWeekStart (weekday == firstWeekday), not Sunday.
- Document that week numbers are locale weekOfYear (consistent with the layout),
not ISO.
- gridDays() and the year-view mini-month dedupe to CalendarGrid.
Verification:
- New KisaniCalTests target with 9 tests, all passing on simulator: exact
Sunday-first June 2026 grid, header↔grid alignment for firstWeekday 1...7,
cell date identity, month navigation (May/Jul/Feb 2026 + Feb 2028 leap +
Dec 2026→Jan 2027), and event-day bucketing (all-day, late-night, midnight-
crossing) across 4 timezones.
- Verified in the running app: header S M T W T F S, May 31 in the Sunday
column, June 1 under Monday, June 24 (Wednesday) selected.
Adds a DEBUG-only KISANI_INITIAL_TAB launch env (compiled out of Release) used
to screenshot the calendar past the sign-in gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Calendar timeline views only populated in Month. Fixes:
- Day/3-Day time grid dropped untimed (midnight) tasks and ignored
recurrence. Now uses occurrences(on:) + hasTime, and adds an all-day
band for untimed tasks, all-day events, and the day's workout.
- Week showed abstract color bars; now lists real task/event titles
(up to 4/day + "N more") via a shared cvAgendaItems helper.
- List view and the task-list toggle now use occurrences(on:) so
recurring tasks appear.
TodayView:
- Slimmer task checkbox (18pt open ring vs 22pt filled donut).
- Confirm before completing a future-dated task (Next 7 Days / Later)
to prevent accidental completion; today/overdue still toggle instantly.
Bump build to 2.0 (9). Add App Store support-site one-pager.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WenzaWatch (new watchOS target, embed disabled until the watchOS platform is
installed — see project.yml note):
- Today list of actionable tasks (dated today/overdue + recurring occurrences),
tap-to-complete with haptics, and add-via-dictation.
- Reads/writes the same tasks through the shared iCloud KV store that
CloudSyncManager mirrors on the phone; watch entitlement uses the iOS app's
kvstore identifier so they share one store. New tasks include all required
TaskItem keys so the phone decodes them.
- NOTE: not compiled here (watchOS SDK absent on this machine); iOS build
verified green with the embed commented out.
Period notifications: day/week/month/year now all fire at midnight (00:00),
adding the daily "One more day passed." (Pretty Progress style).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Renames the user-facing app to Wenza without changing identity:
- CFBundleDisplayName → "Wenza" (app) and "Wenza Widgets" (widget extension).
- Calendar/Health permission prompts now reference Wenza.
- In-app copy updated: sidebar title, Auth + Onboarding screens, Settings
about/help rows, feedback email subject, tutorial text, widget description.
Bundle identifier, App Group, UserDefaults storage keys, team ID, and the
internal Xcode target/project names are intentionally unchanged, so this stays
the same App Store app with no user data loss. App Store listing name must be
changed separately in App Store Connect.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous App Store upload failed on the iPad launch-screen check
because it was a stale archive built before the LaunchScreen fix
(72f4566). Bump CURRENT_PROJECT_VERSION so a fresh archive uploads
cleanly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
App Store upload rejected the bundle: iPad-multitasking apps must declare
a launch screen, but project.yml set an empty UILaunchStoryboardName ("")
and provided no UILaunchScreen.
Add a minimal LaunchScreen.storyboard (blank, systemBackgroundColor) and
point INFOPLIST_KEY_UILaunchStoryboardName at it. Regenerated the project.
Bundle now ships UILaunchStoryboardName=LaunchScreen + compiled
LaunchScreen.storyboardc; builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task context menu (new shared TaskMenuItems) consistent across Today,
Matrix, and Calendar: Pin · Date · Move · Priority · Tags · Add to Live
Activity · Delete. Adds TaskViewModel.setPriority.
Live Activity (ActivityKit):
- TaskActivityAttributes shared between app and widget targets
- LiveActivityManager (start/update/end/toggle, iOS 16.1+)
- TaskLiveActivity widget: lock-screen banner + Dynamic Island
- NSSupportsLiveActivities via project.yml; project regenerated
Calendar events (non-subscription, writable only):
- Edit via system EKEventEditViewController (EventEditView wrapper)
- Delete via new CalendarStore.deleteEvent
Also sweeps in prior in-progress edits already present in the working
tree (AuthManager, NotificationManager, TaskItem, task views) and the
updated ISSUES.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Widgets (new KisaniCalWidgets extension)
- Event Countdown widget: configurable via AppIntent, pick from your events
or set a custom name/date; dot-grid progress toward the event.
- Day Progress and Tasks-Done-Today widgets; lock screen widgets.
- Shared dot grid sizes circles to fill the widget evenly at any count.
- Tasks/calendar prefs now persist to the App Group so widgets read live data.
Health & streaks
- Persist HealthKit authorization and re-establish on launch (fixes the
Today dashboard and streak sync disappearing after relaunch).
- Add a Health permission toggle to onboarding; unify Settings/Profile on the
manager's state.
- Day streak counts from a logged Health workout OR marking all exercises
complete; nudge to mark exercises complete even when Health logged the workout.
Workout editor
- Drag to reorder exercises and move them across sections (drag-and-drop);
swipe to delete.
- Add-exercise sheet: global search and keep-adding-multiple with a running count.
Fixes
- Restore app entitlements link (Sign in with Apple, HealthKit, App Group, iCloud).
- Add HealthKit usage strings; widget Info.plist NSExtension + nested bundle id.
- Calendar "Connect" routes to Settings when access was denied.
- Bake DEVELOPMENT_TEAM and shared versions into project.yml.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Widget extension: 16.0 → 17.0 (containerBackground/widget API require 17)
- xcodegen re-run picks up FloatingTabState.swift (was on disk but
not registered in project.pbxproj after previous xcodegen run)
- KisaniCalWidget: 3 sizes sharing the app's design language
Small: date badge + kisaniCAL. wordmark + task count
Medium: badge + task list (3 items) + workout row
Large: badge + full task list (6 items) + workout row
- WidgetDataStore (Shared/): lightweight Codable types for App Group
UserDefaults cross-process data exchange
- WidgetBridge: TaskViewModel/WorkoutViewModel extensions that sync
data into App Group and call WidgetCenter.reloadAllTimelines()
- ContentView: calls syncWidget() on appear and scene becoming active
- project.yml + entitlements updated; project regenerated via xcodegen