Skip to content

perf: server-side assignee scoping and parallel project fetching in today/upcoming#59

Merged
gnapse merged 1 commit intomainfrom
ernesto/perf-server-side-assignee-scoping
Feb 11, 2026
Merged

perf: server-side assignee scoping and parallel project fetching in today/upcoming#59
gnapse merged 1 commit intomainfrom
ernesto/perf-server-side-assignee-scoping

Conversation

@gnapse
Copy link
Collaborator

@gnapse gnapse commented Feb 11, 2026

Summary

Fixes #57.

  • Server-side assignee scoping: td today and td upcoming now use (assigned to: me | !assigned) in the filter query by default, so the API returns only relevant tasks instead of every team member's tasks. Previously, all tasks were fetched then filtered client-side — in large workspaces this meant 10-15+ paginated API calls with most results discarded.
  • Parallel project fetching: getProjects() now runs in parallel with task pagination via Promise.all, instead of sequentially after.
  • New --any-assignee flag: Opt-in to the old broad-query behavior when you actually want all team members' tasks.
  • Refactored filterByWorkspaceOrPersonal: Switched to named args, accepts optional pre-fetched projects map to avoid redundant API calls.

Benchmark 1: Single-user account (127 today+overdue tasks)

Command Before After Speedup
td today --all 6.15s 1.41s 4.4x
td upcoming 14 --all 3.83s 1.61s 2.4x

Output is byte-for-byte identical between versions.

Benchmark 2: Workspace account (small workspace)

Command Before After Speedup
td today --all 5.70s 2.07s 2.8x
td upcoming 30 --all 4.89s 1.91s 2.6x

Server-side scoping returned 30 tasks vs 109 with --any-assignee (broad query) — the filter eliminates ~72% of irrelevant team tasks even in this small workspace. today output is identical; upcoming human-readable output is identical.

Note

Both benchmarks were run against relatively small accounts. The workspace tested had few team members, so the server-side assignee scoping — while effective — did not exercise the full scenario from the original report (~2 min for 192 tasks across 15+ API pages of team members' tasks). The full expected impact still needs validation against a large shared workspace, where the reduction in paginated API calls should yield a much larger speedup.

Test plan

  • All 685 tests pass
  • New tests for scoped query, --any-assignee broad query, assignee inclusion, parallel fetch
  • Live API: both scoped (default) and broad (--any-assignee) queries return correct results
  • Output correctness: task IDs identical between production and optimized builds
  • Validated in a workspace account (small workspace)
  • Needs validation in a large shared workspace (the scenario from td API slow #57)

🤖 Generated with Claude Code

…oday/upcoming (#57)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnapse gnapse force-pushed the ernesto/perf-server-side-assignee-scoping branch from 0ad0778 to 436f3b9 Compare February 11, 2026 15:51
Copy link

@doist-bot doist-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR delivers substantial performance improvements to the td today and td upcoming commands by implementing server-side assignee scoping and parallelizing project fetching. The changes, which include a new --any-assignee flag, have been thoroughly benchmarked and tested, demonstrating significant speedups and output correctness. No issues were flagged in the inline comments, indicating a high-quality implementation.

Share FeedbackReview Logs

@gnapse gnapse merged commit 94a24b0 into main Feb 11, 2026
7 checks passed
@gnapse gnapse deleted the ernesto/perf-server-side-assignee-scoping branch February 11, 2026 17:28
gnapse added a commit to Doist/todoist-ai that referenced this pull request Feb 12, 2026
…g in get-overview (#310)

# Pull Request

Closes #311. Inspired by Doist/todoist-cli#59.

## Short description

Ports two performance optimizations from the CLI to the MCP server, and
fixes a pre-existing bug found during review:

1. **Server-side assignee scoping in `find-tasks`**: The text/labels
search path was fetching all matching tasks then filtering by
`responsibleUid` client-side. In large workspaces this meant paginating
through many irrelevant team members' tasks. Now uses
`buildResponsibleUserQueryFilter()` to append assignee filters (e.g.
`!assigned to: others`) directly to the API query — matching what
`find-tasks-by-date` already does.

2. **Parallel fetching in `get-overview`**: `generateProjectOverview`
was calling `getProject()`, `getProjectSections()`, and
`getAllTasksForProject()` sequentially. These are independent and now
run in parallel via `Promise.all`.

3. **Bug fix: `filterTasksByResponsibleUser` missing `assigned` mode**
(#311): The `assigned` filtering mode in the container-based path
(projectId/sectionId/parentId) was returning all tasks instead of only
tasks assigned to others.

Note: The container-based search path in `find-tasks` still uses
client-side assignee filtering because `getTasks` doesn't support filter
queries.

### Manual testing

Verified all three affected tools against the live Todoist API using
`scripts/run-tool.ts`:
- `find-tasks` with text search (`{"searchText":"meeting","limit":3}`) —
returns filtered results correctly
- `find-tasks-by-date` with today (`{"startDate":"today","limit":3}`) —
confirmed already correct
- `get-overview` with project ID — returns full project structure
correctly

## PR Checklist

- [x] Added tests for bugs / new features
- [ ] Updated docs (README, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

td API slow

2 participants

Comments