Skip to content

feat: add recurring tasks - #1928

Open
olozano-p wants to merge 20 commits into
usekaneo:mainfrom
olozano-p:feat/recurring-tasks
Open

olozano-p wants to merge 20 commits into
usekaneo:mainfrom
olozano-p:feat/recurring-tasks

Conversation

@olozano-p

@olozano-p olozano-p commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

What changed?

Tasks can now repeat. In the due date picker, turn on Repeat and choose daily, weekly, monthly, yearly, or every N days/weeks/months/years. Weekly rules can run on specific days (e.g. Monday and Thursday). It also works from the create task modal.

To avoid cluttering the general view, only the next task exists. When a repeating task moves to a done column, a copy is created in the project's first open column, due one interval after the completed task's due date (or after the completion time if it had no due date). The repeat rule moves to the new task, so reopening the old one doesn't create another.

  • New nullable recurrence column on tasks (migration 0063), PUT /task/recurrence/{id}, and recurrence on task creation
  • The next task is created from task.status_changed, so it works for status changes, drag and drop, bulk edits and integrations. A row lock makes sure one completion creates exactly one task
  • Dates are calculated in the rule's time zone, so a due date stays on the same local day across DST changes
  • New MCP tool: update_task_recurrence
  • New CLI command: kaneo task repeat <task> [daily|weekly|monthly|yearly], with --every, --on mon,thu, --time-zone (defaults to the computer's) and --stop. In a terminal it opens a picker. kaneo task view shows the rule under "Repeats" and in its JSON, and kaneo doctor checks for the new endpoint
  • Docs: new "Repeat a task" section, the CLI command reference and the CLI agent skill
  • The new strings are translated in all locales

Subtasks aren't copied. If the assignee has left the workspace, the next task is unassigned.

Related issue

Fixes #1918

How did you check it?

  • Unit tests for the date logic (month ends, leap years, DST, weekdays) and the recurrence labels
  • Integration tests: setting and clearing a rule, creating a repeating task, invalid rules, viewers get 403, completion by status change, drag and bulk edit, a single next task under concurrent completions, departed assignee
  • Ran the CI jobs locally: lint, i18n, OpenAPI, typecheck, unit, build, integration, storage, browser and realtime tests
  • CLI unit tests for weekday parsing, rule validation, the "Repeats" label and the task view (snapshots at 80 and 120 columns)
  • Ran the CLI against a local API: set a rule, completed the task and checked the next one kept it, stopped it, the error on a done task, and the interactive picker (with and without Redis), and the upgrade from the latest release
666099952-65d3b14c-9261-43f8-b2df-190757c164b4 Screenshot 2026-10-05 at 20 25 17 Screenshot 2026-10-05 at 20 25 26 Screenshot 2026-10-05 at 20 26 02

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

PR Summary by Qodo

Add recurring tasks across API, web, and MCP

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Let users configure repeating tasks with daily, weekly, monthly, yearly, and custom schedules.
• Create one next task on completion, preserving its rule and local schedule.
• Expose recurrence through MCP, with documentation, translations, and automated tests.
Diagram

graph TD
  Picker["Due date picker"] --> API["Task API"] --> DB[("Task database")]
  MCP["MCP tool"] --> API
  API --> Event["Status event"] --> Dates["Date calculation"] --> Copier["Task duplication"] --> DB
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Schedule or pre-create future tasks
  • ➕ Surfaces upcoming work before the current task is completed.
  • ➖ Requires scheduling or creates excess tasks, contrary to the one-active-occurrence workflow.
2. Share one date-calculation package
  • ➕ Prevents API and web preview arithmetic from drifting.
  • ➖ Adds shared-package wiring for a small, currently tested calculation.

Recommendation: Keep event-driven creation and transactional rule transfer: they cover completion paths while maintaining one active occurrence. Consider sharing the mirrored date calculation if recurrence behavior grows.

Files changed (71) +7986 / -92

Enhancement (33) +1044 / -64
schema.tsType task recurrence storage +2/-0

Type task recurrence storage

• Adds a nullable JSONB task field typed as a recurrence rule.

apps/api/src/database/schema.ts

create-task.tsPersist recurrence during creation +4/-0

Persist recurrence during creation

• Accepts and stores an optional rule on new tasks.

apps/api/src/task/controllers/create-task.ts

duplicate-task.tsTransfer recurrence on completion +87/-17

Transfer recurrence on completion

• Duplicates the task into an open column with new dates and an assignable assignee. A transactional row lock transfers the rule and prevents duplicate successors.

apps/api/src/task/controllers/duplicate-task.ts

get-task.tsReturn recurrence in task details +1/-0

Return recurrence in task details

• Includes the rule when fetching a task.

apps/api/src/task/controllers/get-task.ts

get-tasks.tsReturn recurrence in task lists +1/-0

Return recurrence in task lists

• Includes the rule for listed tasks.

apps/api/src/task/controllers/get-tasks.ts

update-task-recurrence.tsAdd recurrence update controller +36/-0

Add recurrence update controller

• Sets or clears a rule and publishes a task-updated event.

apps/api/src/task/controllers/update-task-recurrence.ts

index.tsRegister recurrence API and subscriber +44/-0

Register recurrence API and subscriber

• Adds a permission-checked PUT endpoint, passes rules on creation, and subscribes to status changes.

apps/api/src/task/index.ts

create-next-occurrence.tsCreate a successor after completion +65/-0

Create a successor after completion

• Finds the first open column, calculates successor dates, and duplicates the completed task. Notifies clients when its rule moves.

apps/api/src/task/recurrence/create-next-occurrence.ts

next-occurrence-date.tsCalculate local successor dates +56/-0

Calculate local successor dates

• Advances dates in the rule's time zone and adjusts start dates or falls back to completion time.

apps/api/src/task/recurrence/next-occurrence-date.ts

schema.tsValidate recurrence rules +56/-0

Validate recurrence rules

• Defines supported frequencies, bounded intervals, unique weekly days, and valid time zones.

apps/api/src/task/recurrence/schema.ts

subscribe.tsHandle status-change events +15/-0

Handle status-change events

• Starts successor creation on task.status_changed outside the completing request's event context.

apps/api/src/task/recurrence/subscribe.ts

response.tsExpose recurrence in responses +3/-0

Expose recurrence in responses

• Adds recurrence to task and board-task schemas.

apps/api/src/task/response.ts

schema.tsAccept recurrence in request schemas +9/-0

Accept recurrence in request schemas

• Allows a rule on creation and a nullable rule when updating.

apps/api/src/task/schema.ts

backlog-task-row.tsxMark repeating backlog tasks +2/-0

Mark repeating backlog tasks

• Shows a recurrence badge in backlog rows.

apps/web/src/components/backlog-list-view/backlog-task-row.tsx

task-card.tsxMark repeating board cards +2/-0

Mark repeating board cards

• Shows a recurrence badge on kanban cards.

apps/web/src/components/kanban-board/task-card.tsx

task-row.tsxMark repeating list tasks +2/-0

Mark repeating list tasks

• Shows a recurrence badge in task list rows.

apps/web/src/components/list-view/task-row.tsx

create-task-modal.tsxCreate repeating tasks from the modal +17/-20

Create repeating tasks from the modal

• Uses the shared picker, submits recurrence, and resets the rule between tasks.

apps/web/src/components/shared/modals/create-task-modal.tsx

task-due-date-picker.tsxCombine due date and repeat controls +91/-0

Combine due date and repeat controls

• Provides date selection, repeat settings, and highlighted upcoming dates for new and existing tasks.

apps/web/src/components/task/task-due-date-picker.tsx

task-due-date-popover.tsxEdit rules from the due-date popover +39/-27

Edit rules from the due-date popover

• Uses the shared picker and submits recurrence changes through a mutation.

apps/web/src/components/task/task-due-date-popover.tsx

task-properties-sidebar.tsxShow repeat status beside dates +4/-0

Show repeat status beside dates

• Adds recurrence indicators to sidebar due-date controls.

apps/web/src/components/task/task-properties-sidebar.tsx

task-recurrence-badge.tsxAdd localized repeat indicator +38/-0

Add localized repeat indicator

• Shows a repeat icon with a localized rule description.

apps/web/src/components/task/task-recurrence-badge.tsx

task-recurrence-settings.tsxConfigure repeat patterns +175/-0

Configure repeat patterns

• Provides standard frequencies, custom intervals, and weekly day selection.

apps/web/src/components/task/task-recurrence-settings.tsx

weekday-picker.tsxSelect localized weekly days +46/-0

Select localized weekly days

• Orders toggles by user preference and requires at least one selected day.

apps/web/src/components/task/weekday-picker.tsx

create-task.tsSend recurrence on creation +2/-0

Send recurrence on creation

• Includes an optional rule in the create-task payload.

apps/web/src/fetchers/task/create-task.ts

update-task-recurrence.tsCall recurrence update endpoint +21/-0

Call recurrence update endpoint

• Adds a fetcher for setting or clearing the task rule.

apps/web/src/fetchers/task/update-task-recurrence.ts

use-create-task.tsForward recurrence through creation +2/-0

Forward recurrence through creation

• Passes the rule from the creation mutation to its fetcher.

apps/web/src/hooks/mutations/task/use-create-task.ts

use-update-task-recurrence.tsSynchronize recurrence mutation caches +49/-0

Synchronize recurrence mutation caches

• Updates task state optimistically, synchronizes board caches, and restores task state on error.

apps/web/src/hooks/mutations/task/use-update-task-recurrence.ts

default-recurrence.tsDefault to local weekly repetition +12/-0

Default to local weekly repetition

• Initializes Repeat using the due date's weekday and browser time zone.

apps/web/src/lib/default-recurrence.ts

format-recurrence.tsFormat readable repeat rules +66/-0

Format readable repeat rules

• Builds localized frequency, interval, and weekday labels.

apps/web/src/lib/format-recurrence.ts

next-occurrence-date.tsPreview upcoming dates +45/-0

Preview upcoming dates

• Mirrors API date arithmetic to highlight future scheduled dates.

apps/web/src/lib/next-occurrence-date.ts

index.tsExpose recurrence on web tasks +3/-0

Expose recurrence on web tasks

• Adds an optional nullable rule to the task model.

apps/web/src/types/task/index.ts

recurrence.tsDefine browser recurrence type +7/-0

Define browser recurrence type

• Models frequency, interval, time zone, and optional weekdays.

apps/web/src/types/task/recurrence.ts

register.tsRegister recurrence MCP tool +42/-0

Register recurrence MCP tool

• Adds a tool to set or clear rules through the API, with interval and time-zone defaults.

packages/mcp/src/tools/register.ts

Tests (6) +676 / -1
next-occurrence-date.test.tsTest recurrence date arithmetic +126/-0

Test recurrence date arithmetic

• Covers intervals, weekdays, month ends, leap years, DST, start-date shifts, and missing due dates.

apps/api/src/task/recurrence/next-occurrence-date.test.ts

create-task-modal.test.tsxTest recurrence during modal creation +25/-1

Test recurrence during modal creation

• Verifies enabling Repeat sends a weekly rule on task creation.

apps/web/src/components/shared/modals/create-task-modal.test.tsx

format-recurrence.test.tsTest localized recurrence labels +43/-0

Test localized recurrence labels

• Checks intervals, weekly day lists, and localized weekday names.

apps/web/src/lib/format-recurrence.test.ts

next-occurrence-date.test.tsTest upcoming-date previews +40/-0

Test upcoming-date previews

• Checks successive month-end dates and custom weekly intervals.

apps/web/src/lib/next-occurrence-date.test.ts

task-recurrence.test.tsCover recurring-task API workflows +412/-0

Cover recurring-task API workflows

• Tests validation, permissions, creation, completion paths, concurrency, client updates, assignees, and dates.

tests/api-integration/task-recurrence.test.ts

mcp-tools.test.tsTest recurrence MCP requests +30/-0

Test recurrence MCP requests

• Verifies custom weekly, default daily, and rule-clearing requests.

tests/api/mcp-tools.test.ts

Documentation (25) +994 / -23
plan-and-execute-tasks.mdxDocument repeating-task behavior +11/-0

Document repeating-task behavior

• Explains configuration, successor creation, dates, copied fields, and stopping a series.

apps/docs/core/functional/plan-and-execute-tasks.mdx

mcp.mdxList recurrence MCP tool +1/-1

List recurrence MCP tool

• Adds update_task_recurrence to the documented task tools.

apps/docs/core/integrations/mcp.mdx

openapi.jsonPublish recurrence API definitions +151/-0

Publish recurrence API definitions

• Documents rule schemas, task fields, creation input, and the update endpoint.

apps/docs/openapi.json

az-AZ.jsonAdd Azerbaijani repeat strings +38/-1

Add Azerbaijani repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/az-AZ.json

de-DE.jsonAdd German repeat strings +38/-1

Add German repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/de-DE.json

el-GR.jsonAdd Greek repeat strings +38/-1

Add Greek repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/el-GR.json

en-US.jsonAdd English repeat strings +38/-1

Add English repeat strings

• Adds controls, interval units, and rule labels; removes the unused clear-date key.

i18n/en-US.json

es-ES.jsonAdd Spanish repeat strings +46/-1

Add Spanish repeat strings

• Adds localized controls and pluralized rule labels; removes the unused clear-date key.

i18n/es-ES.json

fr-FR.jsonAdd French repeat strings +46/-1

Add French repeat strings

• Adds localized controls and pluralized rule labels; removes the unused clear-date key.

i18n/fr-FR.json

hi-IN.jsonAdd Hindi repeat strings +38/-1

Add Hindi repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/hi-IN.json

id-ID.jsonAdd Indonesian repeat strings +30/-1

Add Indonesian repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/id-ID.json

it-IT.jsonAdd Italian repeat strings +46/-1

Add Italian repeat strings

• Adds localized controls and pluralized rule labels; removes the unused clear-date key.

i18n/it-IT.json

ja-JP.jsonAdd Japanese repeat strings +30/-1

Add Japanese repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/ja-JP.json

ko-KR.jsonAdd Korean repeat strings +30/-1

Add Korean repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/ko-KR.json

mk-MK.jsonAdd Macedonian repeat strings +38/-1

Add Macedonian repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/mk-MK.json

nl-NL.jsonAdd Dutch repeat strings +38/-1

Add Dutch repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/nl-NL.json

pl-PL.jsonAdd Polish repeat strings +54/-1

Add Polish repeat strings

• Adds localized controls and Polish plural forms; removes the unused clear-date key.

i18n/pl-PL.json

pt-BR.jsonAdd Brazilian Portuguese repeat strings +46/-1

Add Brazilian Portuguese repeat strings

• Adds localized controls and pluralized rule labels; removes the unused clear-date key.

i18n/pt-BR.json

ru-RU.jsonAdd Russian repeat strings +54/-1

Add Russian repeat strings

• Adds localized controls and Russian plural forms; removes the unused clear-date key.

i18n/ru-RU.json

tr-TR.jsonAdd Turkish repeat strings +38/-1

Add Turkish repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/tr-TR.json

uk-UA.jsonAdd Ukrainian repeat strings +54/-1

Add Ukrainian repeat strings

• Adds localized controls and Ukrainian plural forms; removes the unused clear-date key.

i18n/uk-UA.json

vi-VN.jsonAdd Vietnamese repeat strings +30/-1

Add Vietnamese repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/vi-VN.json

zh-CN.jsonAdd Simplified Chinese repeat strings +30/-1

Add Simplified Chinese repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/zh-CN.json

zh-TW.jsonAdd Traditional Chinese repeat strings +30/-1

Add Traditional Chinese repeat strings

• Adds localized controls and rule labels; removes the unused clear-date key.

i18n/zh-TW.json

README.mdList the MCP recurrence tool +1/-1

List the MCP recurrence tool

• Adds update_task_recurrence to the tool catalog.

packages/mcp/README.md

Other (7) +5272 / -4
0060_military_tana_nile.sqlAdd nullable task recurrence column +1/-0

Add nullable task recurrence column

• Adds a JSONB column for the active task's recurrence rule.

apps/api/drizzle/0060_military_tana_nile.sql

0060_snapshot.jsonRecord migration schema snapshot +5020/-0

Record migration schema snapshot

• Captures the schema with the nullable recurrence column for Drizzle.

apps/api/drizzle/meta/0060_snapshot.json

_journal.jsonRegister recurrence migration +7/-0

Register recurrence migration

• Adds migration 0060 to the Drizzle journal.

apps/api/drizzle/meta/_journal.json

package.jsonAdd API date dependencies +2/-0

Add API date dependencies

• Declares date-fns and @date-fns/tz for time-zone-aware calculations.

apps/api/package.json

package.jsonAdd browser time-zone dependency +1/-0

Add browser time-zone dependency

• Declares @date-fns/tz for upcoming-date previews.

apps/web/package.json

schema.jsonDefine recurrence translation keys +232/-4

Define recurrence translation keys

• Adds schema entries for repeat controls, units, and rule labels; removes the unused clear-date key.

i18n/schema.json

pnpm-lock.yamlLock recurrence date dependencies +9/-0

Lock recurrence date dependencies

• Records API and web declarations for date-fns and @date-fns/tz.

pnpm-lock.yaml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Quick reopening skips the next task 🐞 Bug ≡ Correctness
Description
createNextOccurrence checks the task's current completed state rather than claiming the completion
represented by the event. If someone reopens a recurring task before the asynchronous subscriber
runs or acquires its row lock, the subscriber exits without creating the next task.
Code

apps/api/src/task/recurrence/create-next-occurrence.ts[R25-29]

+      and(
+        eq(taskTable.id, taskId),
+        isNotNull(taskTable.recurrence),
+        taskIsCompleted,
+      ),
Relevance

●●● Strong

Recent accepted concurrency precedent supports fixing stale event guards that can miss or overwrite
status changes.

PR-#1873

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Status changes publish an event after committing, and ordinary event publication does not await its
handlers. Both the initial lookup and the later locked claim require the task to still be completed,
so a reopen between publication and either check discards the completion.

apps/api/src/task/controllers/update-task-status.ts[12-46]
apps/api/src/events/index.ts[27-45]
apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/controllers/duplicate-task.ts[309-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A completion can be followed by a reopen before the asynchronous recurrence handler checks current status, causing that completion to produce no next task.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
- apps/api/src/task/controllers/duplicate-task.ts[304-325]
- apps/api/src/task/recurrence/subscribe.ts[6-13]
## Recommended Fix
Claim or record the recurrence handoff atomically with the completion transition, so subsequent status changes cannot invalidate an already committed completion. Preserve the single-claim guarantee for concurrent completions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Quick reopening skips the next task ✗ Dismissed
Description
createNextOccurrence checks the task's current completed state rather than claiming the completion
represented by the event. If someone reopens a recurring task before the asynchronous subscriber
runs or acquires its row lock, the subscriber exits without creating the next task.
Code

apps/api/src/task/recurrence/create-next-occurrence.ts[R25-29]

+      and(
+        eq(taskTable.id, taskId),
+        isNotNull(taskTable.recurrence),
+        taskIsCompleted,
+      ),
Relevance

●●● Strong

Recent accepted concurrency precedent supports fixing stale event guards that can miss or overwrite
status changes.

PR-#1873

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Status changes publish an event after committing, and ordinary event publication does not await its
handlers. Both the initial lookup and the later locked claim require the task to still be completed,
so a reopen between publication and either check discards the completion.

apps/api/src/task/controllers/update-task-status.ts[12-46]
apps/api/src/events/index.ts[27-45]
apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/controllers/duplicate-task.ts[309-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A completion can be followed by a reopen before the asynchronous recurrence handler checks current status, causing that completion to produce no next task.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
- apps/api/src/task/controllers/duplicate-task.ts[304-325]
- apps/api/src/task/recurrence/subscribe.ts[6-13]
## Recommended Fix
Claim or record the recurrence handoff atomically with the completion transition, so subsequent status changes cannot invalidate an already committed completion. Preserve the single-claim guarantee for concurrent completions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Concurrent edits misdate the next task ✓ Resolved
Description
createNextOccurrence calculates dates from a recurrence and due-date snapshot taken before
duplicateTask locks the source, but the locked claim transfers the rule currently stored there. If
either value is edited between those steps, the inserted task can inherit the new rule with dates
calculated from the old rule or due date.
Code

apps/api/src/task/controllers/duplicate-task.ts[R344-346]

+          startDate: occurrence ? occurrence.startDate : sourceTask.startDate,
+          dueDate: occurrence ? occurrence.dueDate : sourceTask.dueDate,
+          recurrence,
Relevance

●●● Strong

Recent accepted precedents prioritize transactional consistency for concurrent task updates and
duplicate operations.

PR-#1873
PR-#1609

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The first query supplies the dates and rule used for date calculation, whereas the later transaction
claims the current rule and inserts the previously calculated dates. The separate recurrence and
due-date update paths can commit between those operations.

apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/controllers/duplicate-task.ts[309-325]
apps/api/src/task/controllers/duplicate-task.ts[337-346]
apps/api/src/task/controllers/update-task-recurrence.ts[17-21]
apps/api/src/task/controllers/update-task-due-date.ts[20-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Concurrent recurrence or due-date edits can make the next task's dates disagree with the rule it inherits.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-55]
- apps/api/src/task/controllers/duplicate-task.ts[309-346]
## Recommended Fix
Read the source due date and recurrence under the claim lock, then calculate and insert the next dates from those same locked values within the transaction.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Repeat rules set by AI agents land on the wrong weekday ✓ Resolved
Description
The update_task_recurrence MCP tool sets timeZone to "UTC" when the agent passes none, and its
description never tells the agent to pass the user's zone. The web picker saves due dates as local
midnight, so for users east of UTC the stored date falls on the previous UTC day. nextWeekday then
picks the wrong day: for a Berlin user, a Monday task on a Mon/Thu rule comes back due Tuesday.
Editing the rule later in the web picker keeps the stored UTC zone, so the error stays.
Code

packages/mcp/src/tools/register.ts[R797-801]

+        timeZone: nonEmptyString
+          .default("UTC")
+          .describe(
+            "IANA time zone that keeps the next due date on the same local day.",
+          ),
Relevance

●●● Strong

MCP precedents accept fixes where tool contracts or defaults produce incorrect behavior for users.

PR-#1341
PR-#1160

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
nextOccurrenceDate converts the stored due date into recurrence.timeZone before reading
getDay(). With timeZone "UTC", a Monday 00:00 Berlin due date (Sunday 22:00 UTC) counts as Sunday.
With weekdays [1,4] there is no later day after Sunday in a week that starts on Monday, so the code
adds one week and steps back to Monday 22:00 UTC, which is Tuesday in Berlin. The API test (lines
88-96) shows that weekday matching depends on the zone. The web side always sends the browser zone
(default-recurrence.ts), and the settings component keeps the existing timeZone when the rule is
edited.

apps/api/src/task/recurrence/next-occurrence-date.ts[20-38]
apps/api/src/task/recurrence/next-occurrence-date.test.ts[88-96]
apps/web/src/lib/default-recurrence.ts[4-11]
apps/web/src/components/task/task-due-date-popover.tsx[41-47]
apps/web/src/components/task/task-recurrence-settings.tsx[163-166]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `update_task_recurrence` MCP tool falls back to `timeZone: "UTC"`. Due dates are stored as local midnight, so for users who are not on UTC the weekday and month-end logic runs on the wrong local day.
## Fix Focus Areas
- packages/mcp/src/tools/register.ts[780-820]
## Recommended Fix
Remove the `.default("UTC")` and make `timeZone` required whenever `frequency` is given. Validate this in the handler and return a clear error if it is missing. Update the description so the agent knows to pass the user's IANA time zone, for example from their profile or the due date's offset. Another option is to keep the existing rule's `timeZone` when the task already has one.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. A repeating series can stop with no warning to anyone 🐞 Bug ◔ Observability
Description
createNextOccurrence runs from a background task.status_changed subscriber and calls
duplicateTask, which can throw when an older task lacks a newly required custom field with no
default or when description asset copying encounters a storage error. The event bus only logs the
failure with console.error and does not retry it, so the task remains done with its recurrence
rule, no next task is created, and the user receives no error, notification, or activity entry.
Code

apps/api/src/task/recurrence/subscribe.ts[R8-13]

+  async (data) => {
+    // Leave the completing request's context so task.created reaches its own
+    // window too: that client may have refetched the board before this insert.
+    await eventContext.exit(() =>
+      createNextOccurrence(data.taskId, data.userId),
+    );
Relevance

●●● Strong

Recent accepted precedents require retry or durable handling when asynchronous publication fails and
loses important workflow signals.

PR-#1865
PR-#1119

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
subscribeToEvent catches handler exceptions and only calls console.error, while the new
subscriber provides no retry path. duplicateTask validates required custom fields before the
transaction that moves the recurrence rule, and that validation throws if a required field has
neither a value nor a default; a failure there leaves the rule on the completed task without another
attempt or any notice to the person who completed it.

apps/api/src/events/index.ts[54-61]
apps/api/src/task/controllers/duplicate-task.ts[226-242]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/recurrence/subscribe.ts[6-13]
apps/api/src/events/index.ts[45-54]
apps/api/src/task/controllers/duplicate-task.ts[224-236]
apps/api/src/task/validate-task-fields.ts[201-219]
apps/api/src/task/controllers/duplicate-task.ts[304-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
If creating the next occurrence fails because of a missing required custom field, a storage error, or another copy failure, the event bus only logs the error. The recurrence rule stays on the completed task, no next task is created or retried, and the user is not told.
## Fix Focus Areas
- apps/api/src/task/recurrence/subscribe.ts[6-15]
- apps/api/src/task/recurrence/create-next-occurrence.ts[47-64]
- apps/api/src/task/controllers/duplicate-task.ts[224-236]
## Recommended Fix
Catch occurrence-creation errors in the subscriber and log them with task ID and project ID context. Notify the user who completed the task, such as through a notification or an activity entry on the completed task, explaining why the next task could not be created. Retain failures for later processing and make occurrence creation retryable while keeping the locked recurrence claim idempotent. Define how an occurrence copy handles newly required custom fields that cannot be populated from the source, for example by applying defaults or skipping strict required-field validation for occurrences.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (9)
6. Undated tasks ignore their shown weekday ✓ Resolved
Description
defaultRecurrence() leaves weekdays unset, but TaskRecurrenceSettings displays today's weekday
as selected when the task has no due date. If the task is completed on a different weekday,
nextOccurrenceDates advances from the completion time instead, so the next task follows that day
rather than the day shown in the picker.
Code

apps/web/src/components/task/task-due-date-picker.tsx[70]

+            onRecurrenceChange(recurrence ? null : defaultRecurrence())
Relevance

●●● Strong

Concrete recurrence correctness bug; displayed weekday and computed weekday diverge for undated
tasks.

PR-#1762
PR-#1206

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The toggle creates a default weekly rule without weekdays. With no due date, the picker supplies
today's weekday to the settings display, while the API calculates the next date from completion time
when dueDate is null.

apps/web/src/components/task/task-due-date-picker.tsx[52-70]
apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
apps/web/src/lib/default-recurrence.ts[3-11]
apps/api/src/task/recurrence/next-occurrence-date.ts[30-40]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
For an undated task, enabling a weekly rule displays today's weekday as selected even though the rule has no stored weekdays and will repeat according to the eventual completion day.
## Fix Focus Areas
- apps/web/src/components/task/task-due-date-picker.tsx[52-70]
- apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
- apps/web/src/lib/default-recurrence.ts[3-11]
## Recommended Fix
Do not display a weekday as selected for an undated weekly rule unless that weekday is stored in the rule. Explain that an implicit undated rule follows the completion day, or persist an explicit weekday if the picker presents one as selected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Undated tasks ignore their shown weekday ✓ Resolved
Description
defaultRecurrence() leaves weekdays unset, but TaskRecurrenceSettings displays today's weekday
as selected when the task has no due date. If the task is completed on a different weekday,
nextOccurrenceDates advances from the completion time instead, so the next task follows that day
rather than the day shown in the picker.
Code

apps/web/src/components/task/task-due-date-picker.tsx[70]

+            onRecurrenceChange(recurrence ? null : defaultRecurrence())
Relevance

●●● Strong

Concrete recurrence correctness bug; displayed weekday and computed weekday diverge for undated
tasks.

PR-#1762
PR-#1206

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The toggle creates a default weekly rule without weekdays. With no due date, the picker supplies
today's weekday to the settings display, while the API calculates the next date from completion time
when dueDate is null.

apps/web/src/components/task/task-due-date-picker.tsx[52-70]
apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
apps/web/src/lib/default-recurrence.ts[3-11]
apps/api/src/task/recurrence/next-occurrence-date.ts[30-40]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
For an undated task, enabling a weekly rule displays today's weekday as selected even though the rule has no stored weekdays and will repeat according to the eventual completion day.
## Fix Focus Areas
- apps/web/src/components/task/task-due-date-picker.tsx[52-70]
- apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
- apps/web/src/lib/default-recurrence.ts[3-11]
## Recommended Fix
Do not display a weekday as selected for an undated weekly rule unless that weekday is stored in the rule. Explain that an implicit undated rule follows the completion day, or persist an explicit weekday if the picker presents one as selected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. A repeating series can stop with no warning to anyone ✗ Dismissed
Description
createNextOccurrence runs from a background task.status_changed subscriber and calls
duplicateTask, which can throw when an older task lacks a newly required custom field with no
default or when description asset copying encounters a storage error. The event bus only logs the
failure with console.error and does not retry it, so the task remains done with its recurrence
rule, no next task is created, and the user receives no error, notification, or activity entry.
Code

apps/api/src/task/recurrence/subscribe.ts[R8-13]

+  async (data) => {
+    // Leave the completing request's context so task.created reaches its own
+    // window too: that client may have refetched the board before this insert.
+    await eventContext.exit(() =>
+      createNextOccurrence(data.taskId, data.userId),
+    );
Relevance

●●● Strong

Recent accepted precedents require retry or durable handling when asynchronous publication fails and
loses important workflow signals.

PR-#1865
PR-#1119

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
subscribeToEvent catches handler exceptions and only calls console.error, while the new
subscriber provides no retry path. duplicateTask validates required custom fields before the
transaction that moves the recurrence rule, and that validation throws if a required field has
neither a value nor a default; a failure there leaves the rule on the completed task without another
attempt or any notice to the person who completed it.

apps/api/src/events/index.ts[54-61]
apps/api/src/task/controllers/duplicate-task.ts[226-242]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/recurrence/subscribe.ts[6-13]
apps/api/src/events/index.ts[45-54]
apps/api/src/task/controllers/duplicate-task.ts[224-236]
apps/api/src/task/validate-task-fields.ts[201-219]
apps/api/src/task/controllers/duplicate-task.ts[304-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
If creating the next occurrence fails because of a missing required custom field, a storage error, or another copy failure, the event bus only logs the error. The recurrence rule stays on the completed task, no next task is created or retried, and the user is not told.
## Fix Focus Areas
- apps/api/src/task/recurrence/subscribe.ts[6-15]
- apps/api/src/task/recurrence/create-next-occurrence.ts[47-64]
- apps/api/src/task/controllers/duplicate-task.ts[224-236]
## Recommended Fix
Catch occurrence-creation errors in the subscriber and log them with task ID and project ID context. Notify the user who completed the task, such as through a notification or an activity entry on the completed task, explaining why the next task could not be created. Retain failures for later processing and make occurrence creation retryable while keeping the locked recurrence claim idempotent. Define how an occurrence copy handles newly required custom fields that cannot be populated from the source, for example by applying defaults or skipping strict required-field validation for occurrences.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Repeat rules set by AI agents land on the wrong weekday ✓ Resolved
Description
The update_task_recurrence MCP tool sets timeZone to "UTC" when the agent passes none, and its
description never tells the agent to pass the user's zone. The web picker saves due dates as local
midnight, so for users east of UTC the stored date falls on the previous UTC day. nextWeekday then
picks the wrong day: for a Berlin user, a Monday task on a Mon/Thu rule comes back due Tuesday.
Editing the rule later in the web picker keeps the stored UTC zone, so the error stays.
Code

packages/mcp/src/tools/register.ts[R797-801]

+        timeZone: nonEmptyString
+          .default("UTC")
+          .describe(
+            "IANA time zone that keeps the next due date on the same local day.",
+          ),
Relevance

●●● Strong

MCP precedents accept fixes where tool contracts or defaults produce incorrect behavior for users.

PR-#1341
PR-#1160

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
nextOccurrenceDate converts the stored due date into recurrence.timeZone before reading
getDay(). With timeZone "UTC", a Monday 00:00 Berlin due date (Sunday 22:00 UTC) counts as Sunday.
With weekdays [1,4] there is no later day after Sunday in a week that starts on Monday, so the code
adds one week and steps back to Monday 22:00 UTC, which is Tuesday in Berlin. The API test (lines
88-96) shows that weekday matching depends on the zone. The web side always sends the browser zone
(default-recurrence.ts), and the settings component keeps the existing timeZone when the rule is
edited.

apps/api/src/task/recurrence/next-occurrence-date.ts[20-38]
apps/api/src/task/recurrence/next-occurrence-date.test.ts[88-96]
apps/web/src/lib/default-recurrence.ts[4-11]
apps/web/src/components/task/task-due-date-popover.tsx[41-47]
apps/web/src/components/task/task-recurrence-settings.tsx[163-166]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `update_task_recurrence` MCP tool falls back to `timeZone: "UTC"`. Due dates are stored as local midnight, so for users who are not on UTC the weekday and month-end logic runs on the wrong local day.
## Fix Focus Areas
- packages/mcp/src/tools/register.ts[780-820]
## Recommended Fix
Remove the `.default("UTC")` and make `timeZone` required whenever `frequency` is given. Validate this in the handler and return a clear error if it is missing. Update the description so the agent knows to pass the user's IANA time zone, for example from their profile or the due date's offset. Another option is to keep the existing rule's `timeZone` when the task already has one.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Concurrent edits misdate the next task ✓ Resolved
Description
createNextOccurrence calculates dates from a recurrence and due-date snapshot taken before
duplicateTask locks the source, but the locked claim transfers the rule currently stored there. If
either value is edited between those steps, the inserted task can inherit the new rule with dates
calculated from the old rule or due date.
Code

apps/api/src/task/controllers/duplicate-task.ts[R344-346]

+          startDate: occurrence ? occurrence.startDate : sourceTask.startDate,
+          dueDate: occurrence ? occurrence.dueDate : sourceTask.dueDate,
+          recurrence,
Relevance

●●● Strong

Recent accepted precedents prioritize transactional consistency for concurrent task updates and
duplicate operations.

PR-#1873
PR-#1609

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The first query supplies the dates and rule used for date calculation, whereas the later transaction
claims the current rule and inserts the previously calculated dates. The separate recurrence and
due-date update paths can commit between those operations.

apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/controllers/duplicate-task.ts[309-325]
apps/api/src/task/controllers/duplicate-task.ts[337-346]
apps/api/src/task/controllers/update-task-recurrence.ts[17-21]
apps/api/src/task/controllers/update-task-due-date.ts[20-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Concurrent recurrence or due-date edits can make the next task's dates disagree with the rule it inherits.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-55]
- apps/api/src/task/controllers/duplicate-task.ts[309-346]
## Recommended Fix
Read the source due date and recurrence under the claim lock, then calculate and insert the next dates from those same locked values within the transaction.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Repeat edits can fork a completed series ✓ Resolved
Description
updateTaskRecurrence writes the rule by task ID without checking whether duplicateTask has moved
it, while createNextOccurrence calculates dates from an earlier unlocked read. When an edit
overlaps completion, it can restore a rule on the finished task after the next task is created, or
put an edited rule on a new task whose dates were calculated using the old rule.
Code

apps/api/src/task/controllers/update-task-recurrence.ts[R17-20]

+  const [task] = await db
+    .update(taskTable)
+    .set({ recurrence })
+    .where(eq(taskTable.id, id))
Evidence
The endpoint performs a standalone update of the source row. Occurrence creation reads the rule and
computes dates before duplication's transaction locks the source, and that transaction transfers
whichever rule it finds at claim time. An update after the claim can therefore repopulate the
completed row; one before the claim can change the transferred rule without changing the precomputed
dates.

apps/api/src/task/controllers/update-task-recurrence.ts[17-21]
apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-54]
apps/api/src/task/controllers/duplicate-task.ts[304-346]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A recurrence edit concurrent with completion can leave rules on both the completed and next tasks, or pair a new rule with dates calculated from an old one.
## Fix Focus Areas
- apps/api/src/task/controllers/update-task-recurrence.ts[17-21]
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-55]
- apps/api/src/task/controllers/duplicate-task.ts[304-346]
## Recommended Fix
Coordinate edits and occurrence creation under the task row lock. Calculate dates from the rule claimed under that lock, and define how an edit arriving after the rule moves applies to the new task rather than restoring it on the completed source. Test both transaction orderings.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Undated tasks drift from completion time 🐞 Bug ≡ Correctness
Description
createNextOccurrence passes new Date() as the completion time to nextOccurrenceDates instead
of passing the time the status changed. For a task without a due date, any delay before the event
handler reaches this call shifts the next due date later than one interval after completion.
Code

apps/api/src/task/recurrence/create-next-occurrence.ts[53]

+      ...nextOccurrenceDates(task, task.recurrence, new Date()),
Evidence
nextOccurrenceDates uses its completedAt argument when the source has no due date, but the
caller supplies the handler's current time. The status-change payload contains no completion
timestamp, and the subscriber processes that event separately from the status mutation.

apps/api/src/task/recurrence/create-next-occurrence.ts[47-54]
apps/api/src/task/recurrence/next-occurrence-date.ts[41-50]
apps/api/src/task/recurrence/subscribe.ts[6-13]
apps/api/src/task/controllers/task-mutation-effects.ts[72-84]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Undated recurring tasks use occurrence-handler time rather than completion time as the base for their next due date.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[47-54]
- apps/api/src/task/recurrence/subscribe.ts[6-13]
- apps/api/src/task/controllers/task-mutation-effects.ts[72-84]
## Recommended Fix
Capture a completion timestamp when publishing the status change, carry it to the recurrence handler, and pass it to `nextOccurrenceDates`. Add a test in which occurrence processing is delayed after completion.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. AI-set repeats land on the wrong local day ✓ Resolved
Description
The update_task_recurrence MCP tool defaults timeZone to "UTC", and the server computes both the
weekday (date.getDay() on a TZDate) and day/month steps in that zone. For any user outside UTC,
a rule set without an explicit zone matches weekdays against the UTC day: a local-midnight Monday
due date in Tokyo is Sunday in UTC. A local-midnight due date also slides to 23:00 of the previous
day after a DST change, which is the drift the time zone field exists to prevent.
Code

packages/mcp/src/tools/register.ts[R797-801]

+        timeZone: nonEmptyString
+          .default("UTC")
+          .describe(
+            "IANA time zone that keeps the next due date on the same local day.",
+          ),
Evidence
The MCP tool sends timeZone: args.timeZone, which defaults to UTC. nextOccurrenceDate builds
new TZDate(date, recurrence.timeZone) and calls getDay() and addDays/addMonths in that zone,
so the rule's zone decides which weekday and which local day the next task gets. The web client, by
contrast, uses the browser's resolved zone (default-recurrence.ts).

apps/api/src/task/recurrence/next-occurrence-date.ts[20-38]
apps/web/src/lib/default-recurrence.ts[4-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The MCP `update_task_recurrence` tool silently defaults `timeZone` to UTC. The server then matches weekdays and moves dates in UTC, so for non-UTC users the next task can fall on the wrong local day.
## Fix Focus Areas
- packages/mcp/src/tools/register.ts[797-801]
## Recommended Fix
Make `timeZone` required when `frequency` is given, for example with a refine that errors when frequency is set and timeZone is missing. Describe it clearly so agents pass the user's IANA zone. Alternatively, fall back to a workspace or user time zone preference instead of UTC.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. A failed or lost repeat leaves no next task 🐞 Bug ☼ Reliability
Description
createNextOccurrence runs in a fire-and-forget task.status_changed listener, and
subscribeToEvent only logs failures from duplicateTask instead of retrying them. If required
custom-field validation or S3 copying fails, or the process restarts before the listener runs,
completion still succeeds while the task retains its recurrence rule and no next task is created
until someone reopens and completes it again.
Code

apps/api/src/task/recurrence/subscribe.ts[R6-14]

+subscribeToEvent<{ taskId: string; userId?: string | null }>(
+  "task.status_changed",
+  async (data) => {
+    // Leave the completing request's context so task.created reaches its own
+    // window too: that client may have refetched the board before this insert.
+    await eventContext.exit(() =>
+      createNextOccurrence(data.taskId, data.userId),
+    );
+  },
Evidence
The status controller publishes the event after its transaction commits, and the in-process event
bus does not await subscribers; its listener wrapper catches and logs handler errors.
duplicateTask validates copied custom fields and copies description assets to S3 before claiming
the recurrence rule in its transaction, so either operation can fail after an otherwise valid
completion, with no subsequent automatic attempt.

apps/api/src/events/index.ts[50-67]
apps/api/src/task/controllers/duplicate-task.ts[226-240]
apps/api/src/task/controllers/duplicate-task.ts[287-291]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/controllers/update-task-status.ts[17-40]
apps/api/src/events/index.ts[35-60]
apps/api/src/task/recurrence/subscribe.ts[6-13]
apps/api/src/task/controllers/duplicate-task.ts[207-217]
apps/api/src/task/validate-task-fields.ts[202-224]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Next-task creation depends on a best-effort in-process listener. Handler failures or process restarts can leave a completed task holding its recurrence rule without a next occurrence or a signal to the user.
## Fix Focus Areas
- apps/api/src/task/recurrence/subscribe.ts[6-14]
- apps/api/src/task/recurrence/create-next-occurrence.ts[12-55]
- apps/api/src/task/controllers/duplicate-task.ts[304-325]
## Recommended Fix
Make occurrence creation durable and retryable after completion while preserving the existing transactional rule claim for idempotency. Log failures with the task id and context; prevent copied required custom fields from blocking occurrence creation by applying defaults or leaving them empty. Add a periodic sweep in the existing scheduler to retry completed tasks that still hold a `recurrence`, and test that an initial duplication failure recovers without another status change.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

15. Adding repeat to a done task does nothing ✓ Resolved
Description
updateTaskRecurrence only writes the rule, and createTask stores a rule even when the task is
created directly in a final column. The next task is created only from task.status_changed, which
neither path emits. A rule added to a task that is already done, or set on a task created in Done,
never produces a next task unless the task is reopened and completed again, even though the UI shows
it as repeating.
Code

apps/api/src/task/controllers/update-task-recurrence.ts[R17-21]

+  const [task] = await db
+    .update(taskTable)
+    .set({ recurrence })
+    .where(eq(taskTable.id, id))
+    .returning();
Evidence
The only trigger is the task.status_changed subscriber. The recurrence update controller publishes
only task.updated, and create-task inserts recurrence with whatever status was requested.

apps/api/src/task/recurrence/subscribe.ts[6-14]
apps/api/src/task/controllers/update-task-recurrence.ts[17-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A recurrence rule set on a task that is already in a final column, through the update endpoint or at creation, never creates the next task.
## Fix Focus Areas
- apps/api/src/task/controllers/update-task-recurrence.ts[17-31]
- apps/api/src/task/controllers/create-task.ts
## Recommended Fix
After saving a non-null rule, call `createNextOccurrence(task.id, currentUserId)`. It already returns early unless the task is completed and has a rule. Alternatively, reject rules on completed tasks with a 400.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


16. Repeat keeps an outdated weekday after date change ✓ Resolved
Description
defaultRecurrence and withFrequency save weekdays as the due date's weekday, or today's
weekday when no due date is set, at the moment Repeat is turned on. The weekday is not updated when
the due date changes later. In the create modal, turning on Repeat before picking a date, or moving
the due date to another weekday afterwards, makes the series repeat on the old weekday instead of
the due date's.
Code

apps/web/src/lib/default-recurrence.ts[R4-8]

+export function defaultRecurrence(dueDate: Date | undefined): TaskRecurrence {
+  return {
+    frequency: "weekly",
+    interval: 1,
+    weekdays: [(dueDate ?? new Date()).getDay()],
Evidence
The weekday is stored explicitly in the rule. nextWeekday uses the stored weekdays, not the due
date's day, and the picker's onDateChange handlers never touch the recurrence. So a Wednesday due
date with stored [1] repeats the following Monday.

apps/web/src/lib/default-recurrence.ts[4-11]
apps/web/src/components/task/task-due-date-picker.tsx[39-71]
apps/api/src/task/recurrence/next-occurrence-date.ts[20-27]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The default weekly rule fixes `weekdays` to the weekday at the moment Repeat is turned on. Later due date changes leave it stale.
## Fix Focus Areas
- apps/web/src/lib/default-recurrence.ts[4-11]
- apps/web/src/components/task/task-recurrence-settings.tsx[31-41]
## Recommended Fix
Omit `weekdays` in the default rule. The API already repeats a weekly rule without weekdays on the due date's weekday. Store weekdays only once the user explicitly picks days, or, while the stored weekdays still equal the old default, update them when the due date changes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread apps/api/src/task/controllers/update-task-recurrence.ts Outdated
Comment thread apps/api/src/task/recurrence/create-next-occurrence.ts Outdated
Comment thread packages/mcp/src/tools/register.ts
Comment thread apps/api/src/task/recurrence/subscribe.ts
Comment thread apps/api/src/task/controllers/update-task-recurrence.ts
Comment thread apps/web/src/lib/default-recurrence.ts Outdated
@qodo-code-review

qodo-code-review Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Quick reopening skips the next task ✗ Dismissed
Description
createNextOccurrence checks the task's current completed state rather than claiming the completion
represented by the event. If someone reopens a recurring task before the asynchronous subscriber
runs or acquires its row lock, the subscriber exits without creating the next task.
Code

apps/api/src/task/recurrence/create-next-occurrence.ts[R25-29]

+      and(
+        eq(taskTable.id, taskId),
+        isNotNull(taskTable.recurrence),
+        taskIsCompleted,
+      ),
Relevance

●●● Strong

Recent accepted concurrency precedent supports fixing stale event guards that can miss or overwrite
status changes.

PR-#1873

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Status changes publish an event after committing, and ordinary event publication does not await its
handlers. Both the initial lookup and the later locked claim require the task to still be completed,
so a reopen between publication and either check discards the completion.

apps/api/src/task/controllers/update-task-status.ts[12-46]
apps/api/src/events/index.ts[27-45]
apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/controllers/duplicate-task.ts[309-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A completion can be followed by a reopen before the asynchronous recurrence handler checks current status, causing that completion to produce no next task.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
- apps/api/src/task/controllers/duplicate-task.ts[304-325]
- apps/api/src/task/recurrence/subscribe.ts[6-13]
## Recommended Fix
Claim or record the recurrence handoff atomically with the completion transition, so subsequent status changes cannot invalidate an already committed completion. Preserve the single-claim guarantee for concurrent completions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Picking a repeat option wipes custom settings ✓ Resolved
Description
In runTaskRepeat, the frequency picked in the interactive menu is sent to buildRecurrence with
every defaulting to 1, on set to the empty flag list, and timeZone set to localTimeZone();
none of the existing current rule is reused. A user who runs kaneo task repeat KAN-12 on a task
that repeats "every 2 weeks on Monday and Thursday" and selects "Weekly" (labelled "current")
silently saves plain weekly. This drops the interval and weekdays, overwrites the rule's time zone,
and changes the due date of every future task in the series.
Code

packages/cli/src/commands/task/repeat.ts[R79-84]

+            buildRecurrence({
+              frequency,
+              every: Option.getOrElse(options.every, () => 1),
+              on: options.on,
+              timeZone: Option.getOrElse(options.timeZone, localTimeZone),
+            }),
Relevance

●●● Strong

Interactive “current” selection silently discards existing recurrence settings; this is a
deterministic local correctness fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
current is read from the resolved task, but only to label the menu entry as "current". The rule
that is saved is built entirely from the CLI flags and their defaults (every 1, on [], the local
time zone). buildRecurrence adds weekdays only when on is non-empty. The API stores whatever
rule it receives, so the old interval, weekdays and time zone are lost.

packages/cli/src/commands/task/repeat.ts[48-85]
packages/cli/src/tasks/build-recurrence.ts[55-62]
apps/api/src/task/controllers/update-task-recurrence.ts[21-32]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In `kaneo task repeat`, choosing a frequency in the interactive picker (including the one marked "current") rebuilds the rule with interval 1, no weekdays, and the local time zone. This silently throws away the task's custom interval, weekdays and time zone.

## Fix Focus Areas
- packages/cli/src/commands/task/repeat.ts[50-85]

## Recommended Fix
When a flag is not given, fall back to the task's current rule:
- `every`: `Option.getOrElse(options.every, () => current?.interval ?? 1)`.
- `timeZone`: `Option.getOrElse(options.timeZone, () => current?.timeZone ?? localTimeZone())`.
- Weekdays: when `options.on` is empty, the chosen frequency is weekly, and `current?.frequency === "weekly"`, keep `current.weekdays`. Add an optional `weekdays` parameter to `buildRecurrence` for this, or merge the weekdays in afterwards.

Alternatively, if the user picks the current frequency in interactive mode and passes no other flags, keep the existing rule unchanged. Add a test for picking the current frequency on a task that repeats every 2 weeks on Monday and Thursday.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Undated repeats drift after completion 🐞 Bug ≡ Correctness ⭐ New
Description
createNextOccurrence passes new Date() to duplicateTask, so an undated task's next due date is
calculated from when the subscriber processes the event rather than when the task was completed.
Status changes commit before publishing the event, and the event bus does not wait for listeners; a
delay across a day boundary can therefore put the next occurrence on the wrong local day.
Code

apps/api/src/task/recurrence/create-next-occurrence.ts[45]

+    occurrence: { status: openColumn.slug, completedAt: new Date() },
Relevance

●●● Strong

Completion timing deterministically affects undated recurrence; recent event-ordering fixes show
this team accepts such correctness issues.

PR-#1873

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The subscriber supplies a fresh clock value, while the date calculator uses that value whenever the
completed task has no due date. Status mutation publishes only after its transaction, and event
listeners are invoked without being awaited.

apps/api/src/task/recurrence/create-next-occurrence.ts[41-46]
apps/api/src/task/recurrence/next-occurrence-date.ts[44-50]
apps/api/src/task/controllers/update-task-status.ts[16-48]
apps/api/src/events/index.ts[27-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Undated recurring tasks advance from subscriber processing time, which can differ from completion time.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[41-46]
- apps/api/src/task/recurrence/subscribe.ts[6-18]
- apps/api/src/task/controllers/task-mutation-effects.ts[70-81]
## Recommended Fix
Capture the status transition's completion timestamp and pass it through the event and subscriber to `duplicateTask`; use that timestamp for undated occurrences.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


4. Recurring subtasks lose hidden parents ✓ Resolved
Description
createNextOccurrence passes the completing user's ID into duplicateTask, whose parent-relation
query excludes parent projects that user cannot access. A user can complete a recurring child task
without access to its cross-project parent, so the new occurrence is inserted without the existing
parent link.
Code

apps/api/src/task/recurrence/create-next-occurrence.ts[R41-45]

+  const next = await duplicateTask({
+    taskId,
+    currentUserId: currentUserId ?? "",
+    canUpdateTasks: true,
+    occurrence: { status: openColumn.slug, completedAt: new Date() },
Relevance

●●● Strong

Clear relation-preservation bug; repository accepts fixes enforcing task-relation workspace and
duplication boundaries.

PR-#1385
PR-#1609

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Child-task status authorization checks the child's project, whereas the duplication query filters
each parent by the completing user's access. Only relations returned by that query are inserted for
the occurrence; cross-project relations can be created by a user who originally had access to both
projects.

apps/api/src/task/recurrence/create-next-occurrence.ts[41-46]
apps/api/src/task/controllers/duplicate-task.ts[271-289]
apps/api/src/task/controllers/duplicate-task.ts[407-419]
apps/api/src/task/index.ts[516-536]
apps/api/src/task-relation/controllers/create-task-relation.ts[72-84]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An automatic occurrence drops a cross-project parent link when the completing user cannot access the parent project.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[41-46]
- apps/api/src/task/controllers/duplicate-task.ts[271-289]
- apps/api/src/task/controllers/duplicate-task.ts[407-419]
## Recommended Fix
For automatic occurrences, copy the source's existing parent relations independently of the completing user's project-access filter. Keep the filter for user-initiated manual duplication.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (4)
5. Undated tasks ignore their shown weekday ✓ Resolved
Description
defaultRecurrence() leaves weekdays unset, but TaskRecurrenceSettings displays today's weekday
as selected when the task has no due date. If the task is completed on a different weekday,
nextOccurrenceDates advances from the completion time instead, so the next task follows that day
rather than the day shown in the picker.
Code

apps/web/src/components/task/task-due-date-picker.tsx[70]

+            onRecurrenceChange(recurrence ? null : defaultRecurrence())
Relevance

●●● Strong

Concrete recurrence correctness bug; displayed weekday and computed weekday diverge for undated
tasks.

PR-#1762
PR-#1206

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The toggle creates a default weekly rule without weekdays. With no due date, the picker supplies
today's weekday to the settings display, while the API calculates the next date from completion time
when dueDate is null.

apps/web/src/components/task/task-due-date-picker.tsx[52-70]
apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
apps/web/src/lib/default-recurrence.ts[3-11]
apps/api/src/task/recurrence/next-occurrence-date.ts[30-40]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
For an undated task, enabling a weekly rule displays today's weekday as selected even though the rule has no stored weekdays and will repeat according to the eventual completion day.

## Fix Focus Areas
- apps/web/src/components/task/task-due-date-picker.tsx[52-70]
- apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
- apps/web/src/lib/default-recurrence.ts[3-11]

## Recommended Fix
Do not display a weekday as selected for an undated weekly rule unless that weekday is stored in the rule. Explain that an implicit undated rule follows the completion day, or persist an explicit weekday if the picker presents one as selected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. A repeating series can stop with no warning to anyone ✗ Dismissed
Description
createNextOccurrence runs from a background task.status_changed subscriber and calls
duplicateTask, which can throw when an older task lacks a newly required custom field with no
default or when description asset copying encounters a storage error. The event bus only logs the
failure with console.error and does not retry it, so the task remains done with its recurrence
rule, no next task is created, and the user receives no error, notification, or activity entry.
Code

apps/api/src/task/recurrence/subscribe.ts[R8-13]

+  async (data) => {
+    // Leave the completing request's context so task.created reaches its own
+    // window too: that client may have refetched the board before this insert.
+    await eventContext.exit(() =>
+      createNextOccurrence(data.taskId, data.userId),
+    );
Relevance

●●● Strong

Recent accepted precedents require retry or durable handling when asynchronous publication fails and
loses important workflow signals.

PR-#1865
PR-#1119

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
subscribeToEvent catches handler exceptions and only calls console.error, while the new
subscriber provides no retry path. duplicateTask validates required custom fields before the
transaction that moves the recurrence rule, and that validation throws if a required field has
neither a value nor a default; a failure there leaves the rule on the completed task without another
attempt or any notice to the person who completed it.

apps/api/src/events/index.ts[54-61]
apps/api/src/task/controllers/duplicate-task.ts[226-242]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/recurrence/subscribe.ts[6-13]
apps/api/src/events/index.ts[45-54]
apps/api/src/task/controllers/duplicate-task.ts[224-236]
apps/api/src/task/validate-task-fields.ts[201-219]
apps/api/src/task/controllers/duplicate-task.ts[304-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
If creating the next occurrence fails because of a missing required custom field, a storage error, or another copy failure, the event bus only logs the error. The recurrence rule stays on the completed task, no next task is created or retried, and the user is not told.

## Fix Focus Areas
- apps/api/src/task/recurrence/subscribe.ts[6-15]
- apps/api/src/task/recurrence/create-next-occurrence.ts[47-64]
- apps/api/src/task/controllers/duplicate-task.ts[224-236]

## Recommended Fix
Catch occurrence-creation errors in the subscriber and log them with task ID and project ID context. Notify the user who completed the task, such as through a notification or an activity entry on the completed task, explaining why the next task could not be created. Retain failures for later processing and make occurrence creation retryable while keeping the locked recurrence claim idempotent. Define how an occurrence copy handles newly required custom fields that cannot be populated from the source, for example by applying defaults or skipping strict required-field validation for occurrences.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Repeat rules set by AI agents land on the wrong weekday ✓ Resolved
Description
The update_task_recurrence MCP tool sets timeZone to "UTC" when the agent passes none, and its
description never tells the agent to pass the user's zone. The web picker saves due dates as local
midnight, so for users east of UTC the stored date falls on the previous UTC day. nextWeekday then
picks the wrong day: for a Berlin user, a Monday task on a Mon/Thu rule comes back due Tuesday.
Editing the rule later in the web picker keeps the stored UTC zone, so the error stays.
Code

packages/mcp/src/tools/register.ts[R797-801]

+        timeZone: nonEmptyString
+          .default("UTC")
+          .describe(
+            "IANA time zone that keeps the next due date on the same local day.",
+          ),
Relevance

●●● Strong

MCP precedents accept fixes where tool contracts or defaults produce incorrect behavior for users.

PR-#1341
PR-#1160

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
nextOccurrenceDate converts the stored due date into recurrence.timeZone before reading
getDay(). With timeZone "UTC", a Monday 00:00 Berlin due date (Sunday 22:00 UTC) counts as Sunday.
With weekdays [1,4] there is no later day after Sunday in a week that starts on Monday, so the code
adds one week and steps back to Monday 22:00 UTC, which is Tuesday in Berlin. The API test (lines
88-96) shows that weekday matching depends on the zone. The web side always sends the browser zone
(default-recurrence.ts), and the settings component keeps the existing timeZone when the rule is
edited.

apps/api/src/task/recurrence/next-occurrence-date.ts[20-38]
apps/api/src/task/recurrence/next-occurrence-date.test.ts[88-96]
apps/web/src/lib/default-recurrence.ts[4-11]
apps/web/src/components/task/task-due-date-popover.tsx[41-47]
apps/web/src/components/task/task-recurrence-settings.tsx[163-166]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `update_task_recurrence` MCP tool falls back to `timeZone: "UTC"`. Due dates are stored as local midnight, so for users who are not on UTC the weekday and month-end logic runs on the wrong local day.

## Fix Focus Areas
- packages/mcp/src/tools/register.ts[780-820]

## Recommended Fix
Remove the `.default("UTC")` and make `timeZone` required whenever `frequency` is given. Validate this in the handler and return a clear error if it is missing. Update the description so the agent knows to pass the user's IANA time zone, for example from their profile or the due date's offset. Another option is to keep the existing rule's `timeZone` when the task already has one.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Concurrent edits misdate the next task ✓ Resolved
Description
createNextOccurrence calculates dates from a recurrence and due-date snapshot taken before
duplicateTask locks the source, but the locked claim transfers the rule currently stored there. If
either value is edited between those steps, the inserted task can inherit the new rule with dates
calculated from the old rule or due date.
Code

apps/api/src/task/controllers/duplicate-task.ts[R344-346]

+          startDate: occurrence ? occurrence.startDate : sourceTask.startDate,
+          dueDate: occurrence ? occurrence.dueDate : sourceTask.dueDate,
+          recurrence,
Relevance

●●● Strong

Recent accepted precedents prioritize transactional consistency for concurrent task updates and
duplicate operations.

PR-#1873
PR-#1609

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The first query supplies the dates and rule used for date calculation, whereas the later transaction
claims the current rule and inserts the previously calculated dates. The separate recurrence and
due-date update paths can commit between those operations.

apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/controllers/duplicate-task.ts[309-325]
apps/api/src/task/controllers/duplicate-task.ts[337-346]
apps/api/src/task/controllers/update-task-recurrence.ts[17-21]
apps/api/src/task/controllers/update-task-due-date.ts[20-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Concurrent recurrence or due-date edits can make the next task's dates disagree with the rule it inherits.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-55]
- apps/api/src/task/controllers/duplicate-task.ts[309-346]
## Recommended Fix
Read the source due date and recurrence under the claim lock, then calculate and insert the next dates from those same locked values within the transaction.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 6 rules
✅ Cross-repo context — repo relationships
  Explored: repo: usekaneo/drim (sha: f1e40491) — View relationship
Review mode: Auto: 🧠 Deep: Cross-cutting recurrence logic spans API, web, CLI, MCP, and concurrency.

Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit 1024d25

Results up to commit 9883436 🧠 Deep


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Quick reopening skips the next task ✗ Dismissed
Description
createNextOccurrence checks the task's current completed state rather than claiming the completion
represented by the event. If someone reopens a recurring task before the asynchronous subscriber
runs or acquires its row lock, the subscriber exits without creating the next task.
Code

apps/api/src/task/recurrence/create-next-occurrence.ts[R25-29]

+      and(
+        eq(taskTable.id, taskId),
+        isNotNull(taskTable.recurrence),
+        taskIsCompleted,
+      ),
Relevance

●●● Strong

Recent accepted concurrency precedent supports fixing stale event guards that can miss or overwrite
status changes.

PR-#1873

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Status changes publish an event after committing, and ordinary event publication does not await its
handlers. Both the initial lookup and the later locked claim require the task to still be completed,
so a reopen between publication and either check discards the completion.

apps/api/src/task/controllers/update-task-status.ts[12-46]
apps/api/src/events/index.ts[27-45]
apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/controllers/duplicate-task.ts[309-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A completion can be followed by a reopen before the asynchronous recurrence handler checks current status, causing that completion to produce no next task.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
- apps/api/src/task/controllers/duplicate-task.ts[304-325]
- apps/api/src/task/recurrence/subscribe.ts[6-13]
## Recommended Fix
Claim or record the recurrence handoff atomically with the completion transition, so subsequent status changes cannot invalidate an already committed completion. Preserve the single-claim guarantee for concurrent completions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. A repeating series can stop with no warning to anyone ✗ Dismissed
Description
createNextOccurrence runs from a background task.status_changed subscriber and calls
duplicateTask, which can throw when an older task lacks a newly required custom field with no
default or when description asset copying encounters a storage error. The event bus only logs the
failure with console.error and does not retry it, so the task remains done with its recurrence
rule, no next task is created, and the user receives no error, notification, or activity entry.
Code

apps/api/src/task/recurrence/subscribe.ts[R8-13]

+  async (data) => {
+    // Leave the completing request's context so task.created reaches its own
+    // window too: that client may have refetched the board before this insert.
+    await eventContext.exit(() =>
+      createNextOccurrence(data.taskId, data.userId),
+    );
Relevance

●●● Strong

Recent accepted precedents require retry or durable handling when asynchronous publication fails and
loses important workflow signals.

PR-#1865
PR-#1119

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
subscribeToEvent catches handler exceptions and only calls console.error, while the new
subscriber provides no retry path. duplicateTask validates required custom fields before the
transaction that moves the recurrence rule, and that validation throws if a required field has
neither a value nor a default; a failure there leaves the rule on the completed task without another
attempt or any notice to the person who completed it.

apps/api/src/events/index.ts[54-61]
apps/api/src/task/controllers/duplicate-task.ts[226-242]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/recurrence/subscribe.ts[6-13]
apps/api/src/events/index.ts[45-54]
apps/api/src/task/controllers/duplicate-task.ts[224-236]
apps/api/src/task/validate-task-fields.ts[201-219]
apps/api/src/task/controllers/duplicate-task.ts[304-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
If creating the next occurrence fails because of a missing required custom field, a storage error, or another copy failure, the event bus only logs the error. The recurrence rule stays on the completed task, no next task is created or retried, and the user is not told.

## Fix Focus Areas
- apps/api/src/task/recurrence/subscribe.ts[6-15]
- apps/api/src/task/recurrence/create-next-occurrence.ts[47-64]
- apps/api/src/task/controllers/duplicate-task.ts[224-236]

## Recommended Fix
Catch occurrence-creation errors in the subscriber and log them with task ID and project ID context. Notify the user who completed the task, such as through a notification or an activity entry on the completed task, explaining why the next task could not be created. Retain failures for later processing and make occurrence creation retryable while keeping the locked recurrence claim idempotent. Define how an occurrence copy handles newly required custom fields that cannot be populated from the source, for example by applying defaults or skipping strict required-field validation for occurrences.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Concurrent edits misdate the next task ✓ Resolved
Description
createNextOccurrence calculates dates from a recurrence and due-date snapshot taken before
duplicateTask locks the source, but the locked claim transfers the rule currently stored there. If
either value is edited between those steps, the inserted task can inherit the new rule with dates
calculated from the old rule or due date.
Code

apps/api/src/task/controllers/duplicate-task.ts[R344-346]

+          startDate: occurrence ? occurrence.startDate : sourceTask.startDate,
+          dueDate: occurrence ? occurrence.dueDate : sourceTask.dueDate,
+          recurrence,
Relevance

●●● Strong

Recent accepted precedents prioritize transactional consistency for concurrent task updates and
duplicate operations.

PR-#1873
PR-#1609

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The first query supplies the dates and rule used for date calculation, whereas the later transaction
claims the current rule and inserts the previously calculated dates. The separate recurrence and
due-date update paths can commit between those operations.

apps/api/src/task/recurrence/create-next-occurrence.ts[16-32]
apps/api/src/task/recurrence/create-next-occurrence.ts[47-55]
apps/api/src/task/controllers/duplicate-task.ts[309-325]
apps/api/src/task/controllers/duplicate-task.ts[337-346]
apps/api/src/task/controllers/update-task-recurrence.ts[17-21]
apps/api/src/task/controllers/update-task-due-date.ts[20-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Concurrent recurrence or due-date edits can make the next task's dates disagree with the rule it inherits.
## Fix Focus Areas
- apps/api/src/task/recurrence/create-next-occurrence.ts[16-55]
- apps/api/src/task/controllers/duplicate-task.ts[309-346]
## Recommended Fix
Read the source due date and recurrence under the claim lock, then calculate and insert the next dates from those same locked values within the transaction.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Repeat rules set by AI agents land on the wrong weekday ✓ Resolved
Description
The update_task_recurrence MCP tool sets timeZone to "UTC" when the agent passes none, and its
description never tells the agent to pass the user's zone. The web picker saves due dates as local
midnight, so for users east of UTC the stored date falls on the previous UTC day. nextWeekday then
picks the wrong day: for a Berlin user, a Monday task on a Mon/Thu rule comes back due Tuesday.
Editing the rule later in the web picker keeps the stored UTC zone, so the error stays.
Code

packages/mcp/src/tools/register.ts[R797-801]

+        timeZone: nonEmptyString
+          .default("UTC")
+          .describe(
+            "IANA time zone that keeps the next due date on the same local day.",
+          ),
Relevance

●●● Strong

MCP precedents accept fixes where tool contracts or defaults produce incorrect behavior for users.

PR-#1341
PR-#1160

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
nextOccurrenceDate converts the stored due date into recurrence.timeZone before reading
getDay(). With timeZone "UTC", a Monday 00:00 Berlin due date (Sunday 22:00 UTC) counts as Sunday.
With weekdays [1,4] there is no later day after Sunday in a week that starts on Monday, so the code
adds one week and steps back to Monday 22:00 UTC, which is Tuesday in Berlin. The API test (lines
88-96) shows that weekday matching depends on the zone. The web side always sends the browser zone
(default-recurrence.ts), and the settings component keeps the existing timeZone when the rule is
edited.

apps/api/src/task/recurrence/next-occurrence-date.ts[20-38]
apps/api/src/task/recurrence/next-occurrence-date.test.ts[88-96]
apps/web/src/lib/default-recurrence.ts[4-11]
apps/web/src/components/task/task-due-date-popover.tsx[41-47]
apps/web/src/components/task/task-recurrence-settings.tsx[163-166]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `update_task_recurrence` MCP tool falls back to `timeZone: "UTC"`. Due dates are stored as local midnight, so for users who are not on UTC the weekday and month-end logic runs on the wrong local day.

## Fix Focus Areas
- packages/mcp/src/tools/register.ts[780-820]

## Recommended Fix
Remove the `.default("UTC")` and make `timeZone` required whenever `frequency` is given. Validate this in the handler and return a clear error if it is missing. Update the description so the agent knows to pass the user's IANA time zone, for example from their profile or the due date's offset. Another option is to keep the existing rule's `timeZone` when the task already has one.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit fe5a904 ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. Undated tasks ignore their shown weekday ✓ Resolved
Description
defaultRecurrence() leaves weekdays unset, but TaskRecurrenceSettings displays today's weekday
as selected when the task has no due date. If the task is completed on a different weekday,
nextOccurrenceDates advances from the completion time instead, so the next task follows that day
rather than the day shown in the picker.
Code

apps/web/src/components/task/task-due-date-picker.tsx[70]

+            onRecurrenceChange(recurrence ? null : defaultRecurrence())
Relevance

●●● Strong

Concrete recurrence correctness bug; displayed weekday and computed weekday diverge for undated
tasks.

PR-#1762
PR-#1206

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The toggle creates a default weekly rule without weekdays. With no due date, the picker supplies
today's weekday to the settings display, while the API calculates the next date from completion time
when dueDate is null.

apps/web/src/components/task/task-due-date-picker.tsx[52-70]
apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
apps/web/src/lib/default-recurrence.ts[3-11]
apps/api/src/task/recurrence/next-occurrence-date.ts[30-40]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
For an undated task, enabling a weekly rule displays today's weekday as selected even though the rule has no stored weekdays and will repeat according to the eventual completion day.

## Fix Focus Areas
- apps/web/src/components/task/task-due-date-picker.tsx[52-70]
- apps/web/src/components/task/task-recurrence-settings.tsx[158-165]
- apps/web/src/lib/default-recurrence.ts[3-11]

## Recommended Fix
Do not display a weekday as selected for an undated weekly rule unless that weekday is stored in the rule. Explain that an implicit undated rule follows the completion day, or persist an explicit weekday if the picker presents one as selected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread apps/api/src/task/recurrence/create-next-occurrence.ts
Comment thread apps/api/src/task/controllers/duplicate-task.ts Outdated
Comment thread packages/mcp/src/tools/register.ts
Comment thread apps/api/src/task/recurrence/subscribe.ts
@tinsever

tinsever commented Oct 5, 2026

Copy link
Copy Markdown
Member

hi, thanks for contributing to kaneo. This seems like a great idea to add, but in my opinion at least, doubling the size of the calendar isn't something I like. Maybe making it a dropdown would come to mind?

@olozano-p

olozano-p commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

hi, thanks for contributing to kaneo. This seems like a great idea to add, but in my opinion at least, doubling the size of the calendar isn't something I like. Maybe making it a dropdown would come to mind?

Hi! Yes it is sort of a dropdown, sorry the screenshots aren't clear, this is what it looks like before you press repeat:
Screenshot 2026-10-05 at 20 25 09

Then it expands onto the larger shape that the first screenshots show. Is that better? Or you think there's another way?

@olozano-p

Copy link
Copy Markdown
Contributor Author

/agentic_review

Comment thread apps/web/src/components/task/task-due-date-picker.tsx
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit fe5a904

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit fe5a904

@olozano-p

Copy link
Copy Markdown
Contributor Author
recurring-tasks-demo.mp4

I guess seeing it happen is better to get a quick glance for reviewing!

Otherwise I think it's done, you guys let me know what you think 🚀

# Conflicts:
#	apps/api/drizzle/meta/0060_snapshot.json
#	apps/api/drizzle/meta/_journal.json
#	apps/api/package.json
#	apps/api/src/task/controllers/duplicate-task.ts
#	pnpm-lock.yaml
# Conflicts:
#	apps/api/drizzle/meta/0061_snapshot.json
#	apps/api/drizzle/meta/_journal.json
#	apps/api/src/database/schema.ts
#	apps/api/src/task/index.ts
#	apps/docs/openapi.json
#	i18n/az-AZ.json
#	i18n/de-DE.json
#	i18n/el-GR.json
#	i18n/fr-FR.json
#	i18n/hi-IN.json
#	i18n/id-ID.json
#	i18n/it-IT.json
#	i18n/ja-JP.json
#	i18n/ko-KR.json
#	i18n/mk-MK.json
#	i18n/nl-NL.json
#	i18n/pt-BR.json
#	i18n/ru-RU.json
#	i18n/tr-TR.json
#	i18n/uk-UA.json
#	i18n/vi-VN.json
#	i18n/zh-CN.json
#	i18n/zh-TW.json
@olozano-p

Copy link
Copy Markdown
Contributor Author

/agentic_review

Comment thread apps/api/src/task/recurrence/create-next-occurrence.ts
Comment thread apps/api/src/task/recurrence/create-next-occurrence.ts
Comment thread packages/cli/src/commands/task/repeat.ts
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit d85a962

@olozano-p

Copy link
Copy Markdown
Contributor Author

recurring-tasks-demo.mp4
I guess seeing it happen is better to get a quick glance for reviewing!

Otherwise I think it's done, you guys let me know what you think 🚀

@tinsever all ready, here you have a video of the feature in action, let me know what you think or wether you'd do something differently! Thank you for taking care of this repo 💪🏼✨

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recurring tasks

2 participants