feat(completion): explicit completion-date control menu#2147
Open
renatomen wants to merge 15 commits into
Open
feat(completion): explicit completion-date control menu#2147renatomen wants to merge 15 commits into
renatomen wants to merge 15 commits into
Conversation
Add a distinct optional occurrenceDate to the task context menu options, set by occurrence-addressed views (Calendar) and omitted by list/board producers, so occurrence-aware logic never infers occurrence-ness from the overloaded targetDate. Skip now passes the occurrenceDate when present (Calendar), otherwise no explicit date so the service's anchor-aware default resolves it - scheduled for scheduled-anchored recurrences (fixing the List/Kanban 'records today' bug) and today for completion-anchored recurrences (unchanged).
Adds the List/Kanban unskip-label path (occurrenceDate absent + resolved scheduled date already skipped), a due-only recurring case, and a showTaskContextMenu occurrenceDate-forwarding test; makes the completion-anchored assertion timezone-deterministic via getTodayString().
resolveCompletionDate(task, mode, ctx) maps today/asScheduled/onDue/onPicked to a concrete date, reporting unavailability (via i18n reason key) instead of substituting a fallback so the menu can disable an option. asScheduled prefers the clicked occurrence and never falls through to due; completion-anchored recurrences resolve asScheduled to scheduled (explicit re-anchor).
…line updateProperty/updateTaskProperty accept an optional completionDate that overrides the getCompletionDateForTask default (task.occurrence_date || today) for non-recurring status completions. Parameter is optional so all existing status writes and the status-icon path are unchanged; recurring tasks remain unaffected by the existing property===status && !recurring guard. External HTTP/CLI/MCP exposure is deferred.
Replace the single recurring 'Mark complete for this date' item with four explicit actions (Complete today / as scheduled / on due date / on…), and add them net-new for non-recurring tasks. Each resolves via the four-mode resolver and dispatches the native mechanism: recurring -> toggleRecurringTaskComplete; non-recurring -> a status change carrying the completion date. Unavailable modes render disabled with a reason tooltip. Un-complete is per task type: recurring toggles the resolved date out per action; a completed non-recurring task collapses to one 'Mark incomplete' that reverts to the default status. Recurring labels carry an (Instance) prefix. 'Complete on…' opens the shared date picker and records the chosen date (date-only, matching completedDate/complete_instances storage); cancel is a no-op. Adds contextMenus.task.completion i18n keys (en); other locales fall back to English pending translation (tracked in i18n.state.json).
Non-recurring completion/un-completion now surface a neutral failure notice (the old key wrongly said 'recurring task'). Adds tests for the no-completed- status guard, recurring 'Complete on due date' dispatch, and non-recurring 'Complete today' dispatch.
Local runs in non-UTC timezones (e.g. Pacific/Auckland, UTC+13) fail date/ day-boundary tests that pass on CI (GitHub Actions runs in UTC). Set process.env.TZ='UTC' before the config is exported so Jest's worker processes inherit it and read the zone at startup, before any Date is constructed. Overridable via 'TZ=... jest' for timezone-specific testing.
…le reactivates occurrence
Post-review UX refinements:
- Nest the four completion actions (and, for recurring, skip) under a single
'Complete or skip' submenu ('Complete' for non-recurring) to keep the
top-level menu compact; the occurrence note stays top-level.
- Uniform sentence-case labels for both task types (no per-type qualifier):
Completed today / Completed on schedule / Completed on due date /
Completed on (pick date); Skip instance; Mark incomplete.
- Rescheduling a recurring task onto a date removes that date from
complete_instances/skipped_instances, so moving the scheduled date back
reactivates the occurrence without manually editing those lists.
…dule clears instances on or after the new date
- Rename the submenu 'Complete or skip' -> 'Mark complete or skip' ('Complete'
-> 'Mark complete' for the non-recurring variant).
- Rescheduling a recurring task now drops every complete_instances/
skipped_instances entry on or after the new scheduled date (was: exact-date
match only), so an accidental off-schedule completion recorded on a later date
is also cleared when moving the schedule back. Entries strictly before the new
date are preserved as genuine history.
Rescheduling a recurring task that would clear completed/skipped instances (on or after the new date) now prompts for confirmation, listing the affected dates; declining aborts the reschedule entirely (nothing is written). Threaded via an optional confirmClearInstances callback on updateProperty: the context-menu and scheduled-date-modal paths supply a ConfirmationModal; the headless API (and any silent update) omit it and keep the prior behavior. Calendar drag still clears silently (revert-on-cancel deferred).
… menu Adds an appearance setting 'Group complete and skip actions in a submenu' (default on = current submenu behavior). When off, the completion actions (and skip for recurring tasks) render directly in the task context menu instead of nested under 'Mark complete or skip'. Read via settings.completionMenuAsSubmenu, defaulting to the submenu when unset (backward compatible).
In flat menu mode (submenu setting off), wrap the completion/skip actions with a separator before and after so the block is visually delimited from the rest of the task context menu.
The four completion-date actions in the task context menu, the "Group complete and skip actions in a submenu" appearance toggle, and the reschedule-clears-later-instances behavior for recurring tasks. Also maps the completion-menu source files to task-management.md.
This was referenced Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets users choose, per action, which date a task's completion is recorded against — without opening the task.
Screenshots
The four completion actions — Completed today / on schedule / on due date / on (pick date) — under a "Mark complete or skip" submenu ("Mark complete" for non-recurring tasks). A mode renders disabled when its source date is absent (here on schedule, since the task has no scheduled date) rather than silently substituting another date:
The same actions can render flat in the menu (submenu grouping turned off), divider-delimited:
A new appearance setting — Appearance & UI → Task cards → "Group complete and skip actions in a submenu" (default on) — switches between the two layouts:
What's in it
resolveCompletionDate(task, mode, ctx)resolvestoday / asScheduled / onDue / onPickedto a concrete date, reporting unavailability instead of substituting (so options can be disabled).asScheduledprefers the clicked occurrence and never falls through to due; completion-anchored recurrences resolveasScheduledto scheduled (explicit re-anchor).completionDatethreaded through the status pipeline so non-recurring completions record a chosencompletedDate(default today; recurring unaffected by the existingstatus && !recurringguard).Docs
Adds user documentation for the menu and the setting: a Completing Tasks section in
task-management.md, the toggle undersettings/appearance.md, and reschedule-reactivation notes inrecurring-tasks.md(with screenshots indocs/assets/, reusable for release notes).Testing
New/updated unit suites:
completionDateResolver,taskContextMenu.complete,taskContextMenu.pickDate,TaskService(completedDate threading, reschedule-clear, confirm callback),issue-1724. All green; typecheck and lint (ts/architecture) green.Known follow-ups (not blocking)
contextMenus.task.completion.*and appearance-setting keys are English-only; the other locales fall back to English (tracked ini18n.state.json).