Commit Graph

161 Commits

Author SHA1 Message Date
kutesir
dc5c403a36 tasks: fix timed recurring tasks vanishing from Today after their time passes (KC-54)
Real bug: todayTasks excludes timed tasks whose time has passed ('belongs in
overdue'), but overdueTasks deliberately excludes ALL recurring tasks (it feeds
postpone/postponeAllOverdue, which mutate dueDate by id -- for a recurring task
that's the recurrence anchor, not just today's occurrence; including them there
would let Postpone All corrupt the whole series). Recurring tasks fell into the
gap between the two rules and vanished from the Today tab entirely once their
time passed, while Calendar's day view (no time-of-day gating) kept showing
them fine.

Fix: todayTasks only excludes past-time NON-recurring tasks. Recurring
occurrences always stay in Today, matching Calendar, without ever touching the
overdue/postpone path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
7bb37d1995 widgets: add 'This Year' mode to Progress Dots (KC-53)
The Progress Dots lock-screen widget already supports Day/Week/Month via its
configuration picker — no code needed for those. Year was the only mode
missing. Added ProgressDotMode.year, a matching provider branch
(Calendar.dateInterval(of: .year)), and yearTitle(for:), mirroring the
existing day/week/month pattern exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
709eee2579 workout: stop auto-completing the day when sets are ticked (KC-52 follow-up 3)
Real bug, not cosmetic: setAllSetsDone/toggleSet/setExerciseDone still
auto-called logWorkoutCompleted() whenever doneSets == totalSets — leftover
pre-redesign behavior that fought the new completion card. Ticking the last
box (or tapping 'Check all sets') silently counted the day and snapped the UI
to the green 'done' takeover without the user ever hitting Finish.

Removed the auto-log from all three set-mutation methods. Only the card's
explicit Finish Workout/Finish Anyway (-> markWorkoutDone) now counts a day as
done. Check all sets just checks boxes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
65c93769c5 workout: drop seal icon, confirm every destructive quick action (KC-52 follow-up 2)
checkmark.seal.fill (unused elsewhere, reads as a generic 'AI verified badge')
replaced with checkmark.circle.fill, the app's actual convention. All four
state-changing card actions (Finish Anyway, Check All Sets, Clear All Sets,
Didn't Train) now confirm via one PendingAction enum + confirmationDialog
before executing, each with a tailored message; destructive ones use the
.destructive role. The header menu's Clear All Sets (any completion level)
also now confirms instead of executing instantly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
76366a0065 workout: restrained button, finish confirmation, clear toggle (KC-52 follow-up)
Addresses feedback on the completion card: the primary button was a
permanently-filled accent pill (violates 'nothing shouts'/'color earns its
place') and Finish Anyway skipped confirming before overriding real progress.

- QuietAccentButtonStyle: outlined at rest, fills solid accent only while
  pressed.
- confirmationDialog on Finish Anyway showing the logged % (skipped at 100%).
- Check all sets flips to Clear all sets once fully checked — undo is the
  button itself.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
4b2463b017 workout: bring completion controls onto the main page (KC-52)
DotProgressCard becomes a 3-state completion card (pending/done/missed) with a
primary Finish Workout/Finish Anyway action that captures today's workout
honestly — regardless of how many sets got ticked, since Health/Watch already
confirms the workout independently. Secondary actions: check all sets, mark
not completed. Done/missed states show an Undo.

Adds WorkoutViewModel.unmarkWorkoutDone/unmarkWorkoutMissed. Removes 'Mark All
Complete' from the header '...' menu (superseded by the card); keeps 'Clear
All Sets' as a distinct reset utility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
c75a067993 fix(healthkit): use let for interval captured across async let tasks
Build error (Xcode, Swift 6): 'interval' was a var built via two statements
then captured by 3 concurrent async let tasks in dailyReference — Swift 6
requires captured values to be immutable. It was never mutated after setup;
switched to a single-expression let (DateComponents(day: 1)).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
764e1c2f3d fix(analytics): mark WorkoutSnapshot.init(vm:) @MainActor
Build error (Xcode, Swift 6 strict concurrency): WorkoutSnapshot.init(vm:) read
history/workoutDates/schedule/missedDates from @MainActor WorkoutViewModel in a
nonisolated init. The init is only ever called from AnalyticsService.reconcile
(@MainActor) — mark it @MainActor to match its actual call site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
b4813272f6 analytics(healthkit): wire historical HealthKit reference into reports (KC-51 ph4e)
Completes 'available HealthKit trends' for weekly/monthly reports. Adds
HealthKitManager.dailyReference (3 parallel bucketed HKStatisticsCollectionQuery
for steps/active-calories/resting-HR over ~13 months), AnalyticsService.
refreshHealthReference to cache and merge it into DaySamples, and avgSteps/
avgActiveCalories/avgRestingHR on PeriodSummary (nil when absent — honest).
Surfaced in AnalyticsView's weekly/monthly cards.

Verified standalone (swiftc, 8/8): aggregation correct when present, nil when
absent, workout metrics unaffected, reports stay deterministic. Tests added to
AnalyticsEngineTests. This completes all analytics logic — remaining work is the
first Xcode build (device, user-run) to catch any SwiftUI compile issues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
2930557142 analytics(service): capture snapshot value in coordinator closures (concurrency hardening)
Moves sample-building to nonisolated static funcs over a captured WorkoutSnapshot
so the coordinator's escaping closures don't touch @MainActor state — avoids a
main-actor isolation error. Matches the app's existing @MainActor + static shared
pattern (HealthKitManager).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
bcbcda5ba5 analytics(deeplink): route report notification tap to its report (KC-51 ph4c)
Completes deep-link routing (goal #7): AnalyticsDeepLink is Identifiable; a
tapped weekly/monthly report notification is consumed in ContentView on
launch/active and presents AnalyticsView opened to that area. Core round-trip
re-verified standalone; SwiftUI wiring needs an Xcode build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
f8a1f90f8c analytics(ui): AnalyticsView (6 areas + states + charts) + Workout entry (KC-51 ph4b)
Self-contained Analytics UI: Overview/Daily/Weekly/Monthly/12-Month/Exercises
with color+glyph status, empty/partial states, Swift Charts, VoiceOver labels,
Wenza tokens, and the non-medical disclaimer. Entry via an IButton in the
Workout header (sheet). Added Identifiable chart points.

Self-reviewed for compile issues (fixed tuple key-paths, a11y trait literal,
format string) but NOT build-verified — no iOS runtime; SwiftUI/app deps. Logged
in ISSUES.md as device-required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
3f46369078 analytics(app): AnalyticsService + lifecycle reconcile + deep-linked notifications (KC-51 ph4a)
Wires the verified analytics core into the app: AnalyticsService feeds live
WorkoutViewModel data into the reconcile coordinator and exposes UI reads;
ContentView reconciles on launch + foreground; NotificationManager fires one
permission-aware, deduped weekly/monthly report notification carrying an
AnalyticsDeepLink, captured on tap for routing.

NOT build-verified (no iOS runtime; app-module/SwiftUI deps). Logged in
ISSUES.md; historical HealthKit reference + user-scheduled notification time are
follow-ups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
f3213c080e analytics(integration): adapter + reconcile coordinator + deep links (KC-51 ph3)
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>
2026-07-12 22:57:13 +00:00
kutesir
8e73615e8b analytics(persistence): file-based store + reconciliation selection (KC-51 ph2)
Phase 2: AnalyticsStore (JSON files under Application Support) with immutable
frozen daily snapshots + immutable, idempotent, deduped weekly/monthly reports,
and 12-month retention pruning. Engine gains completedWeekStartKeys/
completedMonthKeys/missingKeys for idempotent reconciliation selection.

AnalyticsStoreTests: 6 XCTest cases. Verified standalone via swiftc — 23-
assertion temp-dir harness all pass. Registered files via xcodegen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
31cf38e911 analytics(engine): pure deterministic AnalyticsEngine + models + tests (KC-51 ph1)
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>
2026-07-12 22:57:13 +00:00
kutesir
a4abbb0d4d docs(issues): KC-51 — analytics system plan, persistence eval, blockers
Records the /goal analytics-system implementation plan: reusable-functionality
survey, SwiftData/CoreData/current persistence evaluation, proposed architecture
(stores, immutable reports, pure analysis engine, reconciliation, notifications,
UI, tests), and the two blockers (build-verification unavailable; persistence +
min-iOS decision).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
508d321619 Calendar: force timeline fill (dead space) + long-press menu on blocks
KC-49: week/3-day dead space — the outer .frame only allowed fill; force the
timeline ScrollView greedy (.frame maxHeight .infinity) and pin each timeline
root VStack top, so the header pins to the top instead of floating mid-screen.

KC-50: timeline blocks now have a long-press context menu — Complete, Snooze
(15m/1h/tomorrow), and the shared TaskMenuItems (reschedule/move/priority/
category/edit/delete). Events get no menu. CVTimeBlock takes @ViewBuilder menu.

Logs KC-49 (supersedes KC-48) and KC-50 in ISSUES.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
e8065820b4 docs(issues): log KC-40–48 (streak tally, stats, history, banner, calendar rework)
Records this session's work in the issue tracker: workout streak → lifetime
tally (KC-40), recurring-aware stats (KC-41), Activity History (KC-42), neutral
Today checkbox (KC-43), swipeable streak banner (KC-44), continuous year view
(KC-45), month TickTick bars+collapse reverted (KC-46), week timeline + drag +
grid toggle (KC-47), week/3-day dead-space fix (KC-48).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
51c716ae97 Calendar: fix week/3-day dead space (pin content to top)
The timeline views' content is a thin header + a greedy ScrollView (≈0 ideal
height), so the top-level VStack shrank and the bottomTrailing ZStack floated
it to the bottom — leaving a big empty gap above the day header. Month view
has a tall fixed grid so it never surfaced. Pin the VStack to fill height and
top-align.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
a34afabb2f Revert "Calendar month view: TickTick-style bars + collapse-to-week"
This reverts commit 2ec3278632.
2026-07-12 22:57:13 +00:00
kutesir
d32e55fd44 Revert "Calendar month: drag-to-collapse + full-column selection highlight"
This reverts commit bd43eecfbe.
2026-07-12 22:57:13 +00:00
kutesir
6e20d665fd Calendar week view: timeline ⇄ grid layout toggle
Completes the TickTick weekly reference — a toggle flips the week between the
timeline and a grid of day-cards (2 columns), each card listing that day's
schedule as colored bars. Tapping a card opens that day. Both layouts swipe
by week.

- weekGrid state + toggle button (timeline/grid icons)
- weekGridLayout + weekDayCard

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
39f4c0fcfa Calendar timeline: drag a task block to reschedule its time
The headline of the TickTick weekly timeline — task blocks are now draggable
vertically to change their time, snapped to 15-minute steps. Applies on the
week, 3-day, and day timelines.

- CVTimeItem carries taskID + recurring; task items are tagged, events are not
- CVTimeBlock: a draggable block (GestureState offset while dragging, commits
  on release); events and recurring tasks render fixed (non-draggable)
- rescheduleTask: clamps to the visible hour range and writes the new time via
  taskVM.setDate on the same day

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
7fdf50c898 Calendar week view: 7-day timeline (TickTick-style)
Replaces the mini-month + agenda-list split with a proper weekly timeline —
hours down the left axis, one column per weekday, events as time-positioned
blocks — reusing the same cvTimeGrid/cvDayColumnHeader the day and 3-day
views already use. Horizontal swipe navigates by week (navigateWeek).

Removes the now-unused cvAgendaItems helper.

(Not build-verified — sim runtime removed to reclaim disk; reuses existing
timeline components.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
2441efe304 Calendar month: drag-to-collapse + full-column selection highlight
Two refinements to get closer to the TickTick reference:
- Drag up on the calendar collapses to the selected week; drag down expands
  to the full month (horizontal swipe still changes month). One direction-
  aware DragGesture handles both axes.
- The selected day now gets a rounded full-column highlight behind its
  number + bars when collapsed (as in the tapped-day screenshot), instead
  of only the day circle.

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
8f89d3c73b Calendar month view: TickTick-style bars + collapse-to-week
Matches the referenced TickTick monthly view:
- Day cells now show stacked colored schedule BARS (workout/events/tasks,
  up to 4) instead of dots — a glanceable overview of the month.
- Selecting a day COLLAPSES the grid to just that week, giving the day's
  agenda room below (as in the tapped-day screenshot). A chevron grabber
  expands back to the full month.

- DayCell: bars param + stacked RoundedRectangles, top-aligned, height 54
- monthView: renders week rows (all when expanded, only the selected week
  when collapsed) via weekRows()/selectedWeekRow(); grabber toggles
- eventDots cap raised to 4 for the bar stack

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
23fac88b1d Calendar: header follows the scrolled-to year in continuous year view
The top title now tracks whichever year is at the top of the year scroll
(e.g. scroll into 2027 and the header reads '2027'), instead of staying on
the displayed month. In month/week/day modes it still shows month + year.

- visibleYear state driven by a YearTopKey preference reporting each year
  block's top offset in the 'yearScroll' coordinate space
- headerTitle switches on viewMode
- iOS 16-safe (GeometryReader + PreferenceKey, no scrollPosition API)

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
83d1f1d542 Calendar year view: continuous TickTick-style multi-year scroll
Replaces the single paged year (< 2026 >, with dead space below December)
with an infinite vertical scroll where years flow one after another. Opens
anchored on the current year (accent-colored); scroll up for past years,
down for future. Bottom padding clears the floating tab bar / + button.

- yearView: ScrollViewReader + LazyVStack over a year range, scrollTo(current)
- yearBlock(_:): bold year label + 12 mini-months (reuses cvMiniMonth)
- Removed navigateYear + the ◀ year ▶ header (superseded by inline labels)

(Not build-verified — sim runtime removed to reclaim disk; standard SwiftUI
+ existing cvMiniMonth helper.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
a69b54ff19 Workout streak banner: swipeable week / year / vs-last-week pages
Replaces the single lifetime-tally banner (which used a meaningless streak%7
tick bar) with a 3-page paged TabView:
  1. This week — count + 7 per-day ticks for the current week
  2. This year — count + 12 month ticks, with all-time total underneath
  3. vs last week — this week's count and the delta (▲ more / ▼ fewer / same)

Adds WorkoutViewModel breakdowns: workoutsThisWeek/LastWeek/ThisYear,
currentWeekDays (7 flags), currentYearMonths (12 flags).

(Not build-verified — sim runtime removed to reclaim disk; uses only APIs
already present in the original banner.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
97cc5589cc Today list: neutral checkbox stroke (color already on accent bar + chip)
The Tomorrow/Later rows carry their quadrant color on the left accent bar
and the category chip, so tinting the checkbox too was redundant. Match the
neutral text3 stroke the timeline rows already use.

(Not build-verified — local sim runtime removed to reclaim disk; trivial
one-line swap to an API already used elsewhere in the same file.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
e79cced64a docs: add CICD.md with pipeline flow diagram and gate rules
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
b9d2ca104e docs: add STRUCTURE.md describing targets, layout, and architecture
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
cd2dc1cee6 ci: lowercase runner label (runner registered as self-hosted,macos) 2026-07-12 22:57:13 +00:00
f7bc0b9b3c ci: lowercase runner label (runner registered as self-hosted,macos) 2026-07-12 22:57:13 +00:00
kutesir
cf5dc8943a ci: add Gitea Actions CI/CD, branch workflow, and contributor docs
- .gitea/workflows/ci.yml: build + test KisaniCal scheme on a self-hosted
  macOS runner for every PR into main/develop (and pushes to develop).
- .gitea/workflows/release.yml: archive + export IPA on push to main, with
  a commented TestFlight upload placeholder (App Store Connect API key).
- ExportOptions.plist: export template (Team ID K8BLMMR883, app-store method).
- scripts/gitea-setup.sh: idempotent Gitea API setup for develop default
  branch + main branch protection.
- CONTRIBUTING.md: feature/* -> develop -> main workflow and PR gate rules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
c0dd298e5b Workout streak: lifetime tally that only grows (never resets)
Per user model: the workout count should reflect every workout achieved and
never break. Replaces the consecutive/weekly-goal streak with a simple count
of distinct workout days (totalAchievedWorkoutDays). A below-goal week (4/5)
and a full week (5/5) both count fully (=9); a missed day or blank week never
reduces it; duplicate HealthKit days count once.

- ExerciseModels: streakDays = Set(workoutDates).count
- StreakLogicTests: retargeted to tally semantics (4/5+5/5=9, blank-week,
  duplicates, skipped-day, empty)
- SettingsView: goal caption no longer claims the streak can be lost

NOTE: not built/tested locally — CLI xcodebuild wedges on actool via the
broken CoreSimulator daemon (missing sim runtimes, machine-wide). Verify in
Xcode GUI (prefs now fixed) or on device.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:51:46 +03:00
kutesir
6b5f6bd3ea Stats: schedule-aware weekly streaks, recurring-aware counts, activity history
- 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>
2026-07-05 15:05:14 +03:00
kutesir
dd22e94efe Set build to 2.0 (9)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 01:10:36 +03:00
kutesir
b78b965938 Tasks: square the remaining checkboxes I missed (Today timeline, Next-3, Matrix rows)
Follow-up after the first pass only caught the list-style rows. Converted the
actual tap-to-complete toggles + markers that were still circular:
- Today timeline (tlRows): marker complete/all-day → rounded square (circleD
  18→16); trailing SF-symbol toggle → rounded-square checkbox.
- Today Next-3-Days row: 20pt circle → 16pt rounded square.
- Today "Completed Today" row indicator: green circle → green rounded square.
- Calendar Month agenda (DayTimelineView): trailing SF-symbol toggle → square.
- Matrix quadrant row checkbox: circles → rounded squares.

Left intentionally circular: multi-select sheet (selection, not completion) and
workout-set checkboxes (separate domain).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 01:07:50 +03:00
kutesir
1c325c108e Tasks: unify checkboxes to 16pt rounded square across Calendar & Matrix
Match the Today list checkbox (16pt, cornerRadius 5):
- Calendar list-view row: cr3/18 → cr5/16.
- Calendar day task-list row (CalTaskRow): cr6/22 → cr5/16.
- Calendar Month agenda markers (DayTimelineView): task complete + all-day
  markers circle → rounded square, circleD 18 → 16 (workout stays a circle, the
  timed bullet stays a dot, so those remain visually distinct).
- Matrix row (QDTaskRow): 22 → 16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 00:56:48 +03:00
kutesir
24219f8289 Bump build to 2.0 (10)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 00:40:38 +03:00
kutesir
5a83f232a6 Tasks: square (rounded) checkbox, slightly smaller
TaskRowView checkbox: 18pt circle -> 16pt rounded square (cornerRadius 5),
matching the requested look. Keeps the quadrant-color stroke and 36x44 tap target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 00:28:47 +03:00
kutesir
4a05844ee3 Voice: fix audio-session leak causing insufficientPriority (561017449)
Decoded the device error: NSOSStatusErrorDomain 561017449 = '!pri' =
AVAudioSession insufficientPriority — the mic was denied because a session was
left active (which also blocks the keyboard's own dictation).

Root cause: the early-return guards (recognizer unavailable / invalid format) ran
AFTER setActive(true) but never deactivated, leaking an active session. Also the
.measurement + .duckOthers config is more prone to priority denials.

Fixes:
- Deactivate any stale active session before starting.
- Use .record / .default (least-exclusive recording config).
- stopEngine() now always releases the session (no early-out), and the bailout
  guards call it so the session is never left active.
- Map 561017449 to an actionable message (close other audio apps / restart).

Device-specific; not reproducible on the sim (blocks at the permission dialog).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:56:45 +03:00
kutesir
09acf7afa5 Workout: restore history/missed/compensations from iCloud (KC-39)
save() pushed all 7 workout keys to iCloud KVS, but init only restored 4 —
workout history, missed dates, and rest-day compensations were uploaded yet
never restored, so they were lost on reinstall/update. Added the three missing
restoreIfMissing calls so restore mirrors save.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:52:16 +03:00
kutesir
b6c9f5b0ca Fix: restore token highlighting in Quick Add field (regression)
A fast-path early-return added in the voice-binding fix
(`if tv.textColor == .label && tv.text == text && editing { return }`) skipped
rebuilding the attributed text while editing — so token highlights never
reapplied as the parser updated highlightRanges on each keystroke. Removed it so
highlights always render. Also switched the highlight tint from systemBlue to the
app accent (orange) to match the established design.

Verified on simulator: "Remind me to pray everyday" → "Remind me to" + "everyday"
highlighted in accent orange; chips Today / Daily.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:36:21 +03:00
kutesir
c9f0fd0777 Voice: harden startEngine against "Could not start recording"
Addresses the most common causes of the engine failing to start:
- Defensive teardown before starting (removeTap + stop + reset) — a leftover tap
  from a prior attempt makes installTap throw "already has a tap".
- Validate the input node's hardware format (sampleRate/channels > 0) and fail
  with a clear "No microphone input" message instead of throwing.
- Use inputNode.inputFormat(forBus:0) for the tap.
- Category .playAndRecord + [.duckOthers, .defaultToSpeaker] (more permissive
  than .record with .duckOthers).
- Surface the underlying NSError domain+code in the banner + log to pinpoint any
  remaining device-specific failure.

DEBUG-only KISANI_VOICE_START hook to drive the real engine on a sim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:33:16 +03:00
kutesir
ad304913e4 Voice: show dictated transcript in the Quick Add field
The mic captured speech and the transcript reached rawText, but it never appeared
in the field. NLTaskField.updateUIView (UITextView representable) returned early
when the field wasn't focused (`guard coordinator.isEditing`), so externally-set
text (voice dictation, the normal not-focused case) was dropped and the
placeholder stayed.

Fix: render non-empty `text` even when not editing, placing the caret at the end;
keep the cursor-preserving fast path while actively editing. No new voice system —
this only fixes the existing mic button's transcript→field binding.

Verified on simulator via the real speech.transcript path: "Buy milk tomorrow" →
field shows it (chip: Tomorrow); "Call Romeo at 11 AM" → field shows it (chip:
Today 11:00). Adds DEBUG-only test hooks KISANI_AUTOADD / KISANI_VOICE_SIM
(compiled out of Release). Logged under KC-38.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:22:45 +03:00
kutesir
f8d0a80f17 docs: log KC-38 (voice input — speech-to-task via mic button)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:13:26 +03:00
kutesir
e8c2c5c8ea docs: log KC-32–37 (calendar views, grid alignment, future-task confirm, visual cleanup, dynamic version, everyday recurrence)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:05:33 +03:00