Phase 3 core (verifiable): WorkoutAnalyticsAdapter (app data → DaySample),
AnalyticsCoordinator (idempotent backfill of missing weekly/monthly reports +
retention prune, injectable providers), AnalyticsDeepLink (wenza://analytics
report routing).
Fixed a reconcile bug caught by tests: a retention-boundary week was generated
then immediately pruned, regenerating every run — aligned completedWeekStartKeys
to the prune cutoff → idempotent.
Tests: AnalyticsAdapterTests + AnalyticsCoordinatorTests. Verified standalone via
swiftc (~90 analytics assertions total across engine/store/adapter/coordinator).
Remaining (app wiring, notifications, UI) requires an Xcode build — logged in
ISSUES.md as device-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the analytics system (/goal): the framework-independent analysis
engine. Value-type models (MetricDelta, TrendResult, DaySample, WeeklyReport,
MonthlyReport, ...) and AnalyticsEngine with documented formulas (volume,
%change, consistency, trend) and honest degradation (nil/zero/tiny baseline,
new exercise, rest day, outliers via winsorize, unit-dimension compatibility,
divide-by-zero). Calendar math takes an injected Calendar (locale/firstWeekday/
timezone/DST-safe).
AnalyticsEngineTests: 35 XCTest cases. Independently verified by compiling the
engine with swiftc and running a 42-assertion harness — all pass. (Xcode test
target not runnable in this env; assertions mirror the XCTest file.)
Registered the new files via xcodegen. Logs KC-51 phase-1 progress in ISSUES.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Workout streak now honors the weekly goal ('reach your goal every week'):
counts workout days across consecutive kept weeks, so rest days and a
skipped day within goal no longer reset it to 1 (KC: profile showed
streak 1 with 5 workouts that week).
- Task 7-day bars / 'this week' now count recurring occurrences
(completedOccurrences), fixing permanently-empty bars alongside a
nonzero done rate.
- New task day-streak stat on the profile Tasks card.
- New ActivityHistoryView: 90-day day-by-day archive of completed tasks
(times, repeat markers), workout logs (sets/volume), missed/made-up days.
- StreakLogicTests: 8 unit tests; algorithm additionally verified via
standalone harness (7/7 scenarios incl. the skipped-Thursday case).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parseRecurrence required a space ("every day"), so "everyday" (one word) was
missed. Now \bevery\s*day\b matches both, plus "each day". Added Every Weekday
detection ("every weekday", "weekdays") ahead of the broader weekly/daily
patterns. Sub-day cadence ("every hour"/"hourly") is intentionally NOT matched —
the occurrence engine is day-granular, so it stays a one-off rather than being
mislabeled. 6 parser tests added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8 tests covering daily / weekly / every-weekday / monthly / yearly expansion,
repeat-until cutoff, time-of-day preservation on the mapped local day, and
non-recurring single-day placement. Uses Calendar.current so it holds in any
timezone. Closes the recurrence-coverage gap from the calendar fix.
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>
- Bump LastUpgradeCheck/scheme to Xcode 2620
- Enable DEAD_CODE_STRIPPING, ENABLE_USER_SCRIPT_SANDBOXING, and
STRING_CATALOG_GENERATE_SYMBOLS across targets
- Set CURRENT_PROJECT_VERSION to 8
- Lock iPhone orientation to portrait; normalize iPad orientations
- Use explicitFileType for app/appex product references
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>
- Bars widget: tracked ("Track Countdown") events now also advance — a completed
or past (non-recurring) tracked event falls through to the next-nearest, and the
refresh boundary covers the tracked event's expiry. Recurring tracked events
still roll forward. Completes the KC-26 auto-advance for all paths.
- Progress Dots on the lock screen (accessoryRectangular): show 28 larger dots
instead of 100 tiny ones, render with .primary for the vibrant tint, and use
AccessoryWidgetBackground so it's legible (Home Screen keeps the 100-dot grid).
- Notifications: snooze action labels now read "Snooze 15 min/30 min/1 hour/
2 hours" (lock screen) and "Snooze 15 Minutes…" (in-app Postpone menu).
- New period milestone notifications: recurring "One more week/month/year passed"
at end of week (Sun 20:00), month (1st 09:00), and year (Jan 1 09:00); gated by
a kisani.periodMilestones flag, scheduled via the existing reschedule path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Today ⋯ menu: implemented the placeholder actions.
- Background: 5 selectable themes (Default/Warm/Cool/Mono/Paper), persisted.
- Group & Sort: group by Date (default)/Priority/Category/Quadrant and sort by
Smart/Due/Priority/Title; non-date groupings render via UpcomingSection.
- Select: multi-select sheet with Select All/Deselect All and bulk
Complete/Priority/Move/Delete.
New isolated file TodayMenuFeatures.swift; added TaskViewModel.allActiveRows.
- Matrix "Move" submenu now lists only the other three quadrants (excludes the
current one) using clear matrix labels.
- Event Countdown (Bars) widget auto-advances: each timeline entry re-resolves
"nearest to finish" as of its own time and refreshes at the moment the current
event expires, so it rolls to the next event on expiry/completion.
- Splash wordmark rebranded to "wenza." (regular-weight typewriter + orange dot).
- ISSUES.md: logged KC-23 (Pick Date + time-based reminders), KC-24 (Wenza
rebrand), KC-25 (Move trim), KC-26 (widget auto-advance).
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>
Matrix (KC-21): the Eisenhower Matrix is now a computed view of tasks.
Importance = Priority (High = top row), urgency = deadline proximity, so
tasks auto-place into Q1–Q4. Added a persisted `matrixOverride` so a manual
drag pins the task and syncs its Priority to that row (top → High, moving
down demotes High → Medium) and never gets forced back; editing Priority,
due date, or the editor clears the override to re-place live. New tasks get
KisaniCal priority defaults (birthday/domain/annual/exam/subscription → High,
workout → Medium). AddTaskSheet drops the manual quadrant picker for a
Priority menu (pick Priority + Date only).
Also includes in-progress Progress Dots widget work (day/week/month/custom
event modes, App-Group anchor, recurrence-aware spans).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the dot-grid EventCountdownWidget and TasksCompleteWidget from the
gallery: widget structs, their views, the todayTaskCompletion() helper
(only consumer), and bundle registrations. Shared event plumbing
(EventConfigIntent/Provider/Entry, CycleCountdownUnitIntent) stays for
the surviving Event Countdown (Bars); DotGrid stays for Day Progress.
Remaining widgets untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Groundwork for the slate-background / brand-orange widget re-theme and the
new bar-style event countdown:
- WidgetTheme (dark slate background, brand orange accent, text tones).
- BarGrid: vertical-bar (comb) progress view.
Re-theming the existing widgets and adding the bars widget follows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Recurring tasks only ever showed on their single dueDate. Add a
recurrence engine that expands occurrences per-date with independent
per-occurrence completion.
- TaskItem: recurrenceEnd + completedOccurrences (optional, safe decode).
- TaskViewModel: isOccurrence/occurrenceComplete/toggleOccurrence/
occurrenceCopy/nextOccurrence/occurrences(on:); Daily/Weekly/Monthly/
Yearly/Every-Weekday; respects recurrenceEnd; works across years.
- toggle() routes recurring rows to per-occurrence completion.
- Date filters re-inject per-date occurrence copies; recurring excluded
from overdue. Calendar dots + day detail use occurrences(on:).
Also log KC-11 (year display) + KC-12.
Pending: "repeat until" date UI (engine already honors recurrenceEnd).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Workout tab kept showing the last-used program (with its old
completion) regardless of the weekly schedule, so a rest day still
displayed a stale, fully-checked workout.
- On a new day, the active program now follows the weekly schedule
(applyScheduledProgramForToday in resetSetsForNewDayIfNeeded).
- Add isRestDayToday + a Rest Day card shown when today has no scheduled
program, with a "Work out anyway" override. Header reads "Rest Day".
- Daily set reset (KC-1) unchanged — no day starts pre-checked.
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>
- Add "Upcoming queue" toggle to the Event Countdown widget. When on, it
auto-tracks the Nth nearest upcoming event (Nearest / 2nd … 5th, new
AutoSelectRank enum). When off, track a picked or fully custom event.
- parameterSummary now shows only the relevant fields per mode (toggle +
auto-select, or event/name/date), so the date input only appears for
custom events.
- Replace the dense 380-day placeholder with a ~90-day window so the
gallery preview shows a legible dot grid.
(The interactive task-checkbox widget already exists as "My Tasks".)
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>
Introduce CalendarStore.shared so Today, Calendar, and Onboarding read
the same auth status and event cache instead of each spinning up its own
EKEventStore. Trust the system's authoritative authorization status over
the granted bool, debounce repeat permission requests, route denied users
to Settings, and prompt when enabling local calendars without access.
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>
- My Tasks widget: today's tasks with interactive check-off (ToggleTaskIntent)
and a "+" that opens the app's add-task sheet; writes preserve all task
fields and sync via the App Group.
- Event Countdown: AZURE/AWS-style header (accent name + countdown label +
percent); tap the label to cycle days → weeks → time remaining.
- Today: collapsible "Completed" archive section below Upcoming.
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>
- Add NSUbiquitousKeyValueStore entitlement to KisaniCal.entitlements
- New CloudSyncManager: mirrors UserDefaults to iCloud KV Store on every save; restores to UserDefaults on fresh install when local data is missing; observes external changes to pull updates in real-time; guards against 1 MB limit
- TaskViewModel: restore from iCloud on init, push to iCloud on every save
- WorkoutViewModel: restore programs/schedule/activePid/completedDates from iCloud on init, push all four keys on every save
- ContentView: restoreSettings() + backupSettings() on appear and foreground — syncs ~18 small settings keys (appearance, tabs, tutorial flags, calendar prefs, workout notifications)
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
- TutorialManager: tracks 4 steps (NLP, Calendar, Matrix, Workout)
persisted via AppStorage; shown once then never again
- TutorialView: fullscreen overlay with even-odd spotlight cutout,
animated accent border, ultraThinMaterial callout card, pill progress
dots, Next/Get started button, and Skip
- TutorialFrameKey: PreferenceKey collects CGRect for each anchor index;
.tutorialAnchor() modifier wired to FAB (step 0) and all 4 tab buttons
- Callout auto-positions above or below the spotlight based on screen half
- ContentView collects frames via onPreferenceChange and layers the overlay
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
- Remove all personal sampleTasks from TaskViewModel (tasks start empty)
- Namespace UserDefaults keys by Apple ID for task and workout data isolation
- Add ProfileSetupView for name capture after first Sign in with Apple
- Add updateDisplayName() to AuthManager
- Route to ProfileSetupView when user has no display name yet
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
Adds four UIApplicationShortcutItems registered dynamically at launch:
• Add Task — opens the add-task sheet immediately, any tab
• Today — switches to the Today tab
• Calendar — switches to the Calendar tab
• Workout — switches to the Workout tab (only registered if the
workout tab is enabled in settings; re-registers when the setting changes)
Cold-launch and foreground-activation paths both handled via AppDelegate.
ShortcutHandler singleton bridges UIKit callbacks to SwiftUI via @Published.
- 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