Backlog
For completed tasks, see 911-completed.
Priority Legend
[P1]- High priority: bugs, blockers, quality issues affecting UX[P2]- Medium priority: improvements, polish, technical debt[P3]- Low priority: future enhancements, ideas, nice-to-have
Bugs [P1]
Fix progressive loading not working (load more broken) (2026-01-02)
- Location:
HistoryGrid.swift:287-324(LoadMoreTrigger) - Root cause: SwiftUI
Gridis not lazy, soonAppearfires immediately on render, not on scroll - Fix: Replaced
onAppearwith GeometryReader viewport detection - Result: Grid height dropped from 3904px to 1986px (50% reduction)
- Related: 803-load-more-bug
- Location:
Fix cache race condition in FileStorageService (2026-01-03)
- Location:
FileStorageService.swift:384-428 - Root cause:
updateRecord()deleted old file before writing new, causing data loss on error - Fix: Write new file first, then delete old file, then update cache atomically
- Location:
Fix AppState tag mutation inconsistency (2026-01-03)
- Location:
MinutaApp.swift:379-397,447-469 - Root cause:
loadData()had optimization that skipped updates if tag count/IDs matched, missing property changes - Fix: Always update tags and rebuild dictionary on load; use direct assignment instead of optional chaining
- Location:
Fix concurrent image operations (2026-01-03)
- Location:
RecordEditorViews.swift:453-509 - Root cause: Rapid image adds could race on
currentRecord - Fix: Added
isImageOperationInProgressguard to prevent concurrent add/delete operations
- Location:
Debug OKLCH colors
- Investigate hue distribution across alphabets
- Check color consistency between views
Fix view of tags in edit form
- Tags display incorrectly in RecordEditor
- Location: RecordEditorViews.swift
Fix play button position when keyboard is undocked
- Floating button positioned wrong when iPad keyboard is undocked/floating
Fix window not hiding immediately on app close (Mac)
- Window stays visible briefly after closing the app
- Should hide immediately when user closes
Investigate empty block above running timers (2026-03-24)
- Unexplained empty space appearing over working timers section
- Need to identify the cause and remove
Test Coverage [P1]
Current: 361 tests passing (unit tests)
Add tests for HistoryGrid progressive loading
- Location:
HistoryGrid.swift:173-282 - Test
displayedRecordCountstate management:- Initial load shows 50 records
loadMore()adds 50 more records- Count doesn’t exceed total records
- Test reset behavior on filter change:
- Resets to 50 when records count drops (new filter)
- Preserves count when records increase (e.g., new record added)
- Test
LoadMoreTriggervisibility:- Shows when
hasMoreToLoadis true - Hidden when all records displayed
- Shows when
- Test
visibleMonthGroupsreturns correct subset - Could use ViewInspector or UI tests with performance fixture
- Location:
Add unit tests for StorageLocationManager
- Document folder selection untested
- Add tests with mock FileManager
Add UI test for image attachment
- Test adding image to a record
- Verify image thumbnail appears in record row
- Screenshot to confirm visual appearance
- Location:
UITests/Flows/
Add App Intents/Shortcuts integration tests
- StartTimerIntent, StopTimerIntent have no coverage
- Location: AppIntents.swift
Add view tests for multiplatform project
- Location:
Minuta/(needs test target in project.yml) - ViewInspector for SwiftUI testing
- ContentView, SettingsSheet tests
- Location:
Add SVGReportRenderer internal tests
- Test private methods: renderTimeSeriesChart, wrapText, truncateText
- Current tests only cover public API
Audit Findings (2026-01-03) [P1]
Dead Code:
Remove unused
dayCellSizeconstant (2026-01-03)- Removed from CalendarDatePicker.swift
Remove unused
UIKitimport from ContentView (2026-01-03)- Location:
ContentView.swift:2 - No UIKit types used in file
- Location:
Remove unused(2026-01-03)UIKitimport from SettingsSheet- False positive:
UIPasteboardis used for copy-to-clipboard functionality
- False positive:
Remove unused DateRangePickerView.swift (2026-01-03)
- Location:
Views/History/DateRangePickerView.swift - 114-line file never referenced - CalendarDatePicker is used instead
- Location:
Remove unused Color(oklch:) initializer (2026-01-03)
- Location:
Color+Hex.swift:30-40 - Never called; only Color(hex:) is used
- Also removed unused contrastingTextColor(for: OKLCH) overload
- Location:
Maintainability:
Extract layout constants in CalendarDatePicker (2026-01-03)
- Location:
CalendarDatePicker.swift:75-91 - Created
LayoutMetricsenum with named constants for cellSize, fonts, padding, opacity, etc.
- Location:
Refactor duplicated date range logic in CalendarDatePicker (2026-01-03)
- Added
endOfDay()andsetDateRange(from:to:)helper methods - Reduced duplicate “add day, subtract second” pattern from 5 occurrences to 1
- Added
Clean up verbose comments in CalendarDatePicker (2026-01-03)
- Removed redundant section labels and implementation detail comments
- Kept useful explanatory comments (corner radius logic, week selection criteria)
UI Tests (16 failures as of 2026-04-15):
Fix UI test failures - History section not found
- Affected:
RecordEditingTests.testViewExistingRecords,RecordEditingTests.testShareSheetStaysOpen,RecordEditingTests.testHistoryRecordEditAndDismiss - Root cause:
app.staticTexts["History"].waitForExistence(timeout: 5)fails — History section not appearing in time - Location:
RecordEditingTests.swift:17,RecordEditingTests.swift:146
- Affected:
Fix UI test failures - Export sheet not opening
- Affected: all 7
ReportFlowTests(testExportSheetShowsAllFormats,testPDFReportPreview,testPNGReportPreview,testSVGReportPreview,testCSVExportPreview,testExportSheetStaysDuringGeneration,testCancelExportSheet), all 3ReportWithImagesTests(testPDFReportWithImages,testPNGReportWithImages,testSVGReportWithImages) - Root cause:
navigateToExportSheet()fails waiting for History section - Location:
ReportFlowTests.swift:186,ReportWithImagesTests.swift:90
- Affected: all 7
Fix UI test failures - Missing fixture data in settings
- Affected:
SettingsTests.testViewExistingTags - Root cause: “Acme Corp” tag not found — standard fixture tags not loading into settings view
- Location:
SettingsTests.swift:72
- Affected:
Fix UI test failures - Tag management flow
- Affected:
TagManagementTests.testNewTagCreatedOnTimerStop,TagManagementTests.testTimerWithNewTag - Root cause: unknown, needs investigation
- Location:
TagManagementTests.swift
- Affected:
Code Quality [P2]
Remove debug cellBorder() from HistoryGrid (already done)
- Location:
HistoryGrid.swift:54-75 - Already wrapped in
#if DEBUG- no borders shown in release builds
- Location:
Split AppState into smaller services
- Location:
MinutaApp.swift(320+ lines) - Handles: state, tags, records, deletion undo, tag merging
- Consider: TagManagementService, RecordManagementService
- Location:
Split RecordEditorViews
- Location:
RecordEditorViews.swift:1-450(450+ lines) - Mixed concerns: display, editing, formatting, image operations
- Extract image handling to separate component
- Location:
Consolidate history reload triggers (2026-01-03)
- Merged two
.taskmodifiers into one for initial load - Added comments explaining each reload trigger
- Location:
ContentView.swift:113-126
- Merged two
Performance [P2]
Layout optimization
- Profile and reduce view complexity across History, Today, and filter sections
- Identify unnecessary re-renders and layout passes
Improve HistoryGrid performance
- Location:
HistoryGrid.swift:173-282 - Current: 50 initial + 50 per batch (LoadMoreTrigger fixed 2026-01-02)
- Known issues:
- Double grouping:
allMonthGroupsandvisibleMonthGroupsboth callgroupRecordsByMonth - Full re-grouping on each
loadMore()instead of appending to existing groups - Nested
Gridis not lazy - even 50 records creates 50+ views upfront onChange(of: records.count)may over-trigger re-renders
- Double grouping:
- Potential fixes:
- Cache
allMonthGroupsand derivevisibleMonthGroupsby slicing - Incremental loading: append to existing groups instead of re-compute
- Consider
LazyVStackwith custom sticky headers instead of nestedGrid - Debounce filter changes to avoid rapid re-computation
- Pre-compute day groups and slice by index rather than re-grouping
- Cache
- TODO: Profile and identify additional bottlenecks
- Benchmark targets: Launch <2.0s, Scroll (6 swipes) <8.8s
- Related: 801-optimization-plan Phase 6, 802-scroll-profiling, 803-load-more-bug
- Location:
Investigate slow year report generation
- Year range report is noticeably slow (about 500 records)
- Profile SVGReportRenderer and WebKitReportService
Add pagination for large record sets
- Location:
FileStorageService.swift:218-256 - All records loaded into memory - problematic for years of data
- Location:
UI/UX Improvements [P2]
- Add 300ms debounce to record saving while editing
- Avoid saving on every keystroke; debounce input changes by 300ms before persisting
- Location:
RecordEditorViews.swift(onChange handlers)
Edit record sheet
- Current: floating editor positioned above the clicked row
- Replace with a proper sheet (modal or slide-over)
- Location:
HistoryGrid.swift,TodaySectionView.swift
Better controls for date/time selection in record editor
- Current text fields are bare; need proper date/time pickers
- Consider inline pickers or popovers for start/end time editing
Keyboard accessibility
- Arrow keys for tag navigation, Tab for all controls
- Mac: Shift-Tab for tab character, Tab for navigation
Arrow key navigation in CalendarDatePicker
- Navigate between year/month/week/day/preset buttons with arrow keys
- Enter/Space to select
- Location:
CalendarDatePicker.swift
Tab focus on tag filter buttons
- Tags in TagFilterView should be focusable via Tab key
- Location:
TagFilterView.swift
Support drag and drop for image attachments
- Drop images onto RecordEditor from Finder, Photos, browsers
Apply color theme to whole project
- Consistent theming, design tokens, dark mode consistency
Save everything on Cmd+S
Create app logo
- iOS and Mac icon, App Store assets
Infrastructure [P2]
Fix GitHub Actions
- Figure out good way to run and fix setup
Monorepo structure (turborepo or bazel)
- Build and screenshots cached, triggered on code changes
- Use code hash as cache key
Publishing [P2]
Research iOS app publishing from Uzbekistan
- App Store Connect requirements, payments, tax implications
Trademark protection for “Minuta”
- Research registration in software category
- Consider jurisdiction (US, EU, local)
macOS Enhancements [P3]
Minified/PiP mode (Mac only)
- Compact floating view showing only running timers
- Toggle via
pipicon in title bar toolbar (next to pin/settings) - Layout: small window with just timer list + play button
- Behavior:
- Remembers minified state separately from main window size
- Works with existing pin (always-on-top) feature
- Hides History section, Today section, settings button
- Shows: running timers, floating play button, expand button
- Expand button (
pip.exit) returns to full view
- Implementation:
- Add
isMinifiedstate to WindowPinManager (rename to WindowStateManager?) - Add pip toolbar button in SceneDelegate
- Conditionally render sections in ContentView based on minified state
- Animate window resize transition
- Store minified window frame separately in UserDefaults
- Add
- Related: pairs well with pin button for “mini always-on-top timer”
Menu Bar app
- Status item showing running timers
- Quick start/stop actions
- Click to summon/hide always-on-top widget
Always-on-top widget (superseded by Minified mode above)
- Floating window with current timer state
- Minimal, non-intrusive design
Widgets [P3]
Home Screen / Desktop widgets (WidgetKit) (2026-01-05)
- Small: running timer with stop button, or today summary with start button
- Medium: running timer + quick start tags with interactive buttons
- Large: running timers list with stop buttons, today summary, quick start tags grid
- App Groups for shared data (
group.tools.minuta.app) - Timeline provider with 1-minute updates for running timers
- Interactive buttons using App Intents (start/stop timers)
- Note: Widget extension disabled in build until App Groups configured in Developer portal
Lock Screen widgets (iOS) (2026-01-05)
- Circular: gauge showing running timer progress or today’s hours
- Rectangular: running timer with tag name and duration, or today summary
- Inline: compact text showing timer or today’s total
Enable widget extension (requires Apple Developer portal setup)
- Register App Group:
group.tools.minuta.app - Register bundle ID:
tools.minuta.app.widgetswith App Groups capability - Enable App Groups for
tools.minuta.app - Uncomment widget lines in
project.yml - Restore App Groups entitlement in
Minuta.entitlements
- Register App Group:
iPhone nightstand mode widget
Watch App [P3]
- Apple Watch companion
- Start/stop timers, show current state
- Complication for quick access
- WatchConnectivity for sync
Integrations [P3]
iCloud sync
- Architecture is ready, needs implementation
Automerge for conflict-free sync
- CRDT library (automerge-swift v0.6.1)
- Decision: Use Automerge over custom CRDT implementation
- See 505-automerge-migration-plan for 11-phase implementation plan
Deel timesheet sync
- See 701-deel
Google Calendar import
- Convert calendar events to time records
System calendar auto-tracking
- Auto-create records from meetings
Architecture [P3]
Introduce view model layer
- Reduce direct AppState coupling from views
Implement repository pattern
- Abstract storage implementation details
Stop storing tag colors
- Tag colors are deterministically derived from tag names via
TagColorGenerator(OKLCH-based) - Remove
colorfield from the storedTagmodel; compute on read instead - Drop migration logic that carries forward legacy stored colors
- Location:
Shared/Sources/MinutaShared/Models/Tag.swift, call sites in views
- Tag colors are deterministically derived from tag names via
Create shared reports library
- Extract report generation (SVG/PDF/PNG/CSV) into a reusable module consumable by both app and CLI
- Location:
Shared/Sources/MinutaShared/Services/(ReportService, SVGReportRenderer, WebKitReportService)
Websites [P3]
- Docs and promo sites
- SvelteKit + IDS styles
- Static generation with backlinks component
- Prepare scaffold project with Daler
CalendarDatePicker [P3]
Use full month title instead of abbreviated (2026-01-03)
- Changed from
shortMonthName()(MMM) tomonthName()(MMMM)
- Changed from
Round borders only for range start/end days (2026-01-03)
- Added
RoundedCornersshape for selective corner rounding - Start day: left corners rounded, End day: right corners rounded
- Middle days: no corners, Single day: all corners
- Added
Use pressable button style for navigation (2026-01-03)
- Added
PressableButtonStylewith scale and opacity animation - Applied to all interactive elements
- Added
Use pressable button style for presets (2026-01-03)
- Applied
PressableButtonStyleto Today, Week, Month preset buttons
- Applied
Add drag-to-select date range (2026-01-03)
- Drag across calendar days to select a range
- Handles dragging in both directions (forward and backward)
Toggle visual state for all selectable elements (2026-01-04)
- Year/month/week labels and presets show “selected” visual when their range matches
- Single
rangeBinding(from:to:)factory method creates bindings for any date range - Added
SelectableLabelToggleStyle,PresetToggleStyle,WeekNumberToggleStyle - All styles now use
LayoutMetricsconstants andPressableButtonStyle
Fix calendar control layout
- Add folded variant (collapsed view)
- Make year and month labels wider
- Set default state to today (not expanded full calendar)
- Fix overall layout alignment and spacing issues
Replace calendar toggler with simple button
- Current: FilterToggleButton (capsule toggle) to show/hide calendar
- Change to: plain button that opens calendar on tap
- Location:
HistorySection.swift
TagFilterView [P3]
All tags unselected by default
- Currently all tags are selected on first open; change default to no tags selected (show all records unfiltered)
Rearrange filter components
- Review layout and order of CalendarDatePicker, tag filters, and summary controls
- Improve visual hierarchy and discoverability
Add drag-to-select for tag filters
- Press and drag across tags to toggle selection
- Similar to calendar drag-to-select behavior
Inbox
Unsorted ideas. Review periodically.
- Play button background gradient in OKLCH with hours marks
- Logo: play button with gradient and hours/minutes marks, 3D watch concept
- Sort new timers consistent with file system
- Heatmap view for records (monthly calendar visualization)
- Auto start/stop based on app activity (macOS, accessibility permissions)
- Better timeline navigation (swipe gestures, scrubber)
- Native Apple Charts in history view (iOS 16+ / macOS 13+)
- Not sure if we need to show Today section when it falls within the selected date span — experiment with hiding it
Related
- 911-completed - Completed tasks archive
- 912-audit-2025-12-25 - Audit report
- 901-2025-12-08-init - Project initialization
- 902-2025-12-09-editor - Editor implementation