Repository navigation
v0.10.0 — Stability & Quick Add
This release delivers a major stability overhaul with 120+ bug fixes across 8 focused passes, plus a new Quick Add Task dialog for faster task creation.
✨ New Features
Quick Add Task Dialog
- Global quick-add dialog accessible from sidebar, mobile topbar, and
qkeyboard shortcut - Smart project defaulting: current URL → last-used → first non-archived
- Main form: Title, Project, Due Date, Reminder
- Expandable "More Options": Priority, Labels, Assignee, Description
- Inline kanban task creation now auto-opens the full task sheet immediately
🔒 Security Fixes
- IDOR via backup restore: skip unmapped foreign IDs to prevent cross-user data injection
- Label IDOR: validate label ownership on task create, update, and bulk add-label
- Editor privilege escalation: PATCH /api/projects now requires owner role for all settings
- Time session ownership: ownership check on DELETE prevents cross-user session deletion
- MCP categoryId validation: ownership check prevents unauthorized category access
- Push subscription scoping: upsert scoped to userId prevents cross-user push registration
- AI prompt injection: separate system prompt from user content in AI endpoints
- AI input validation: Zod schema caps input lengths on generate-subtasks
- AI error leak: only forward expected 403/404 messages; generic message for all others
- AI rate limiting: 10 req/min per-user sliding window on AI endpoints
- Backup export scoping: restrict to owned projects only, preventing shared project data leakage
- Member enumeration: member DELETE returns 404 (not 403)
- User search restriction: restricted to project owners only
- MCP role enforcement: update_project requires owner role (was editor)
🛠 Bug Fixes — Data Integrity
- MongoDB operator mixing: wrap bare fields in
$setalongside$push/$unsetin task PATCH and complete routes - OAuth race condition: guard
User.createandAccount.createagainst E11000 duplicate-key errors - Backup restore: split prepare/insert phases with flag-based compensating rollback
- Backup restore: create ProjectMember records and clean up orphaned ones
- Account deletion cascade: fix shared project tasks/members/notes cleanup
- Merge
$unsetreminderAt into single DB write to prevent stale SSE events - Fix recurring task order userId scoping
- Cascade-delete Notification records on all 6 task deletion paths
- Cascade-delete Notes and Notifications in MCP delete handlers
- Remap orphaned tasks before updating columns to prevent phantom column state
- Label DELETE cleanup no longer incorrectly scoped by userId (fixes shared projects)
- Preserve assigneeId when creating recurring task successor
- MCP notes: wrap update in
$setto prevent document replacement - statusHistory push conditional on actual column change
- Recurring task order uses
max(order)+1instead ofcountDocuments
🛠 Bug Fixes — State Machines & Rendering
- Bulk move sets/clears completedAt based on last column
- Recurrence only spawns child on incomplete→complete transition
- completedAt clearing on reopen fixed (
undefined→null) - DnD cancel reverts optimistic column change on drop-outside/Escape
- DnD cross-column + reorder concurrent write resolution
- Task sheet syncs selectedTask after DnD column move
- Task title/description SSE sync with useEffect guards and debounce cleanup
- Flush title+description as single update on unmount
- NoteEditorDialog: flush pending auto-save on dialog close
- Sync TaskSheet form state from prop changes
- selectedTask syncs with tasks array via useEffect
- handleDragEnd reads from tasksRef for latest state
- Bulk action selects controlled with post-action reset
- Priority sort uses
find().lean()instead ofaggregate()(fixes ObjectId serialization) - Command palette deep links auto-open task sheet via openTaskId prop
- Command palette saves recent searches only on result selection
- Archived tasks sheet hides mutation buttons for viewer role
- Calendar task creation uses offlineFetch for offline support
🛠 Bug Fixes — Background Jobs & Offline
- Offline sync: permanent 4xx failures removed from queue instead of infinite retry
- Offline queue:
navigator.locksprevents double-replay race with Background Sync - Dual sync paths: app-level sync defers to Background Sync when available
- SSE reconnect: reset retry counter on online event
- SSE stream
cancel()callback for cleanup on dropped connections - Service worker notification actions include X-Session-Id header
- Store tab sessionId with offline mutations for correct SSE echo suppression
- Badge API feature detection in service worker
- Notification worker: filter
dismissed:falsein dedup query - Reminder dedup: unique compound index prevents duplicate notifications
- scheduleNextReminder uses retry + reminderAt restore on failure
- Preserve reminderAt during preference recalculation
- Overdue notifications dismissed when task rescheduled to future date
- Compensating delete on ProjectMember.create failure during project creation
- Active timer guard before bulk archive
- Always update local state after deleteSession regardless of
navigator.onLine - markSaved() moved out of finally block to reflect actual success
- Atomic timer start with
$not/$elemMatchprecondition - Owner membership creation uses atomic upsert (fixes race condition)
🎨 Style
- Updated app icons to pillar column design with purple-to-cyan gradient squircle
📊 Stats
- 87 files changed, 3,526 insertions, 504 deletions
- 120+ individual bug fixes across 8 focused passes
- All 2,013 tests pass across 184 test files