Repository navigation
Releases
v0.11.0 — Collaboration, Reports & Hardening
This release adds a wave of collaboration and reporting features — task comments with @mentions, task dependencies, a cross-project My Work view, time-tracking reports, and a subscribable iCal calendar feed — on top of a large security and stability hardening pass that resolves ~60 findings from a full-repo code review ahead of going public.
✨ New Features
💬 Task comments with @mentions (#5)
- Per-task discussion thread visible to all project members (viewers can read and post)
@mentionsvalidated as project members, each firing a new mention notification- Realtime via SSE comment events; offline-aware via
offlineFetch - Author or project owner can delete; mention matching is word-boundary/span-masked so a short name won't match inside a longer name or email
🔗 Task dependencies — "blocked by" (#6)
- A task can be blocked by other tasks in the same project and can't be completed while any blocker is open
- Cycle detection (3-colour DFS) on update; completion guard enforced across REST PATCH,
/complete, MCPcomplete_task/update_task, and bulk move-to-done - Blocked badge (open-blocker count) on the card + a cycle-aware "Blocked by" picker in the task sheet
- Deleting a task prunes its id from every other task's
blockedByon all delete paths — no phantom blockers
🗓 iCal (.ics) calendar feed (#9)
- Subscribe to your task due dates from Apple / Google / Outlook Calendar
- Public feed
GET /api/calendar/[token]/feed.icsauthenticated by a high-entropy (256-bit) secret URL token (Google "secret address" model) - Per-user revocable
calendarFeedToken; settings API to enable / regenerate / disable - Exposes only your own non-archived tasks with a due date; RFC-5545 builder with ICS-injection-safe escaping, UTC all-day dates, and 75-octet line folding
🧭 "My Work" cross-project view (#8)
- Open tasks assigned to you across all accessible projects, grouped by project or by due-date bucket (Overdue / Today / This week / Later / No date)
- Reuses the existing access-enforced tasks API (no backend change); deep-links to the task in its project
- Overdue conveyed via text (not color alone); dark-mode-safe
📊 Time-tracking reports (#7)
- Turns captured task time sessions into a summary: total, per-project bars, and a weekly trend over a trailing window of N ISO weeks
GET /api/stats/time-report: access-scoped aggregation counting only your own sessions; range selector (1–52 weeks, default 8)- No chart dependencies — CSS/inline bars with empty/error/loading states and a11y labels
🎯 Per-column WIP limits (#2)
- Optional per-column work-in-progress limit set in the column manager (empty = no limit)
- Header shows count / limit; over-limit columns get a destructive ring + badge and an aria-label
- Soft limit — drag is flagged, not blocked; count uses true column size so board filters can't mask an over-limit column
📅 Quick-select chips in the date picker (#4)
- Today / Tomorrow / Next week / Clear chips in the date picker popover for one-click due dates
- Chips resolve to a local calendar date reusing the same
yyyy-MM-ddformatting as the calendar — no timezone shift; purely additive
🔒 Security Fixes (#1)
- User enumeration:
users/searchreturns exact-email matches only — no directory enumeration - OAuth pre-hijack: refuse silent OAuth linking to an unverified credentials account
- Cross-user data deletion: scope backup-restore task/notification deletion to owned projects only (previously deleted tasks in shared projects the user only viewed)
- Secrets/config: fail-fast on missing
AUTH_SECRET; lock down mongo-express in the dev compose - Token lifetime: access tokens get a bounded expiry (default 90d, max 365d); JWTs voided after password change / account deletion; shorter session
maxAge - Rate limiting & password policy: in-memory rate limiting on register/login/password-change + a stronger password policy
- Error/CSP hardening: map duplicate-key to 409 and stop leaking internal error messages; 400 for malformed ObjectId params; drop
unsafe-evalfrom the production CSP - SSE: re-validate the session on heartbeat + a 1h max stream lifetime
- Account deletion: unset dangling assignee refs and notify affected members
🛠 Bug Fixes (#1)
Data integrity
- Replace a mis-declared sparse compound unique index with a partial index; move overdue dedup to a generation-based key and swallow
E11000on summary/digest - Reset the cached Mongoose connection promise on rejection;
syncIndexes()on worker boot so the dedup index actually replaces the old one on existing deployments - Add an
isRunningoverlap guard to the worker interval; restoreNotificationPreferenceon backup rollback
Recurrence & tasks
- Claim completion atomically (
findOneAndUpdateoncompletedAt:null) to stop duplicate recurring spawns; emit the spawned occurrence's created event to the originating tab - Validate
columnIdagainstproject.columnson REST create/update and per-project on bulk move; remap orphaned tasks on MCP column changes - Unify recurrence math on
date-utilsand anchor monthly recurrence (no drift); schedule a reminder for the spawned occurrence; two-pass DST correction incomputeReminderDate - Exclude archived tasks from stats counts; flag overdue only after the due day passes in the user's timezone
Offline & sync
- Offline PATCH echoes the real id and hooks merge it — no more blank/corrupted entities;
tempId → realIdreplay mapping and stable replay order with an idempotency key - Treat auth redirects as transient (keep the queue), skip caching
/api/events, clear page cache on logout, guardcache.puton redirects - SSE reconnect retries indefinitely and recovers on focus/visibility; guard against duplicate
EventSourceconnections; refetch notes/presets/members/archived on reconnect - Fix the stuck user-search spinner + out-of-order results; roll back
dismissAllon failure - Auth: fail open in the JWT session-revocation check so a transient DB error can't force-log-out every user
UI & accessibility
- Store due dates at UTC midnight from the card popover, bulk bar & calendar drag (timezone fix); clear
?taskId=after first open so a closed sheet stays closed - Don't overwrite an in-progress title/description edit on SSE update
- Confirm bulk delete and list-item delete; block deleting a column that still holds archived tasks
- Surface errors on subtask add, project settings, column save, calendar create, and bulk-create
- Dark-mode chips, keyboard-openable rows, aria-labels, truncation,
Cmd/Ctrl+K, calendar project selector, locale-correct labels - Share dialog falls back to email when a non-collaborator has no name to show
📓 Docs & Internal
- Added interaction coverage for the bulk-actions bar — assign/unassign, add-label, priority, move, and archive now assert the right callbacks fire (#3)
- Added a full-repo code review & bug-hunt report (4 critical / ~14 high / ~20 medium / ~20 low) with a phased remediation plan
- Cleaned up internal development artifacts and fixed docs conventions before going public
v0.10.1 — Icon Refresh
Updated all application icons to match the unified purple-to-cyan gradient theme used across Calcflux, Regex-Lab, and Home-Maintenance.
🎨 Style
- Full-bleed maskable icons (no built-in rounded corners — OS applies its own mask)
- Added
icon.svgfor scalable display - Set
"purpose": "any maskable"on PWA icons in manifest - Updated theme/background colors to dark (
#0F172A) - Renamed icon files to match cross-project convention (
icon-192.png,icon-512.png) - Updated all references in service worker, notification settings, and manifest
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
v0.9.2
What's Changed
- ci: Simplify Docker build workflow to amd64-only — removes matrix strategy, artifact passing, and merge job (113 → 46 lines)
v0.9.1
What's Changed
- fix: Replace QEMU emulation with native ARM runners in Docker build workflow — resolves arm64 builds hanging due to illegal instruction crashes under QEMU
- docs: Update README, CLAUDE.md, and marketing page for recent features
- feat: Close TaskSheet on Mark Complete with undo toast
- feat: Implement 5 quick-win features with tests and E2E coverage
- feat: Pin Complete & Delete actions to sticky footer in TaskSheet
- fix: Replace hidden md:* with max-md:hidden for Tailwind v4 compatibility
v0.9.0
What's New
Features
- Filter presets — Save and load filter configurations per project
- Global search API — Search across tasks, projects, and categories from the command palette
- Enhanced command palette — Recent searches, improved results, keyboard-driven workflow
- Inline task editing — Edit task titles directly on Kanban cards without opening the sheet
- Kanban keyboard navigation — Navigate and manage tasks with arrow keys and shortcuts
- Bulk task operations — Enhanced bulk actions bar with more operations
- Context menu — Right-click context menu on task cards
- TaskSheet Mark Complete — Closes the sheet with an undo toast for quick completion
- shadcn Calendar pickers — Replaced native date inputs with polished Calendar and DateTimePicker components
- Auto-save indicator — Visual feedback in TaskSheet when changes are being saved
- Dashboard hover animations — Scale animation on metric cards
Bug Fixes
- Fixed Android PWA notification actions both triggering snooze instead of complete
- Fixed
reminderAtmissing from server-side task serialization - Hardened service worker notification actions with credentials and error logging
- Fixed Tailwind v4 compatibility (
hidden md:*→max-md:hidden)
Docs
- Updated README, CLAUDE.md, and marketing landing page to reflect all new features
- Added Command Palette and AI Subtasks cards to the marketing features section
- Fixed test count (184 files, 2013 tests) and AI provider reference in CLAUDE.md
v0.8.1 — Sticky task action footer
What's New
Sticky footer for task actions
The Mark Complete / Reopen button and secondary actions are now pinned to the bottom of the task sheet, accessible from any scroll position.
Before: Complete and Delete buttons were buried at the bottom of 12+ scrollable form sections — on mobile, users had to scroll through everything to reach them.
After:
- Mark Complete / Reopen button is always visible in a sticky footer bar
- Three-dot overflow menu contains Archive, Duplicate, Share, and Delete
- Form content scrolls independently above the footer
- Destructive actions (Delete) are tucked behind the menu for intentional friction
Details
- Restructured
SheetContentlayout:overflow-hiddenouter container withflex-1 overflow-y-autoscrollable area + pinned footer - Replaced stacked full-width action buttons with compact footer bar +
DropdownMenu - Overflow menu opens upward (
side="top") so it doesn't get clipped by the viewport - Conditional menu items: Archive (only when completed), Duplicate, Share
- All 1861 tests passing across 175 test files
v0.8.0
What's New
Features
- Calendar date pickers — Replaced native
<input type="date">and<input type="datetime-local">with shadcn Calendar-based pickers (DatePicker and DateTimePicker) for consistent cross-browser styling on due date, reminder, and recurrence end date fields - Auto-save indicator — TaskSheet now shows a save status indicator
- Dashboard hover animation — Metric cards on the dashboard animate on hover
Bug Fixes
- Empty task rows on Overview page — Fixed Tailwind CSS v4 source-order issue where
hidden md:gridfailed to show desktop layouts; replaced withmax-md:hiddenpattern across 8 components - Android PWA notification actions — Fixed both notification actions triggering snooze instead of distinguishing between snooze and complete
v0.7.5
Fixes
- Reminder persistence: Include
reminderAtin server-side task serialization so the bell icon and reminder data persist after page refresh - Cleanup: Remove stale Google Calendar references from backup test
v0.7.4
Fixes
- SW notification actions: Added explicit
credentials: "same-origin"to service worker fetch calls ensuring session cookies are sent for Mark Complete and Snooze actions - Error visibility: Added
handleActionError()helper withconsole.error()logging and detailed error response body reading for easier debugging
Tests
- Added 3 E2E Playwright tests verifying complete, snooze, and auth redirect detection through the real HTTP stack
- Added 4 integration tests verifying the full notification-worker → snooze/complete flow with real MongoDB