Repository navigation
feat(repeat): month days, repeat query, skip last ends repeat - #232
Merged
Merged
Conversation
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
Repeating Tasks v3 (
.scratch/recurring-tasks-v3/spec.md), three related improvements to recurring tasks and projects.1. Month days (
monthDays)Monthly rules could only advance on the scheduled date's day-of-month, and a chain anchored on the 31st got clamped to 28/30 in short months and never recovered.
RepeatRule.monthDays?: number[](1–31, ascending, deduped) in the shared DTO; only meaningful forunit=month.normalizeRepeatRulefilters/sorts/dedupes and dropsmonthDaysfor other units;canonicalRepeatRulekey order becomesunit, interval, weekdays?, monthDays?, anchor, until?. Missing optional keys are omitted, so existing canonical strings and derived IDs are unchanged (locked by regression tests).nextMonthlyOccurrence: anchor month is month 0, everyinterval-th month is active, days past the end of a month clamp to its last day (so[31]means "last day" and does not drift). NomonthDays→ original path, behavior unchanged.RepeatRuleDtogainsmonthDaysvalidation; agentrepeatRuleSchema, descriptions and error message updated. No DB migration (repeatRule is a JSON string).RepeatRuleFieldshows a 1–31 day grid for month mode, with hints for the default (scheduled day) and 29–31 clamping; switching units stripsmonthDays.formatRepeatSummaryrenders month days, showing 31 as "last day".2. Natural-language repeat query
packages/shared/src/repeat-query.ts(parseRepeatQuery/parseRepeat) reusing the when-query lexicon, now extracted intopackages/shared/src/query-lexicon.ts(normalization, numbers, time extraction, weekday/month names). The existing when-query tests pass unchanged.每天/daily,每周五/every fri,每周一三五,every 2 weeks on wed,每个工作日,每周末,每月15号,每月1号和15号,每月最后一天,每两个月10号,每年3月8日,完成后每3天/3 days after completion, plus optional times.WhenCandidategains arepeatbranch;parseWhenQuerytakesallowRepeat. Queries starting with a repeat trigger (每/完成后/every/each) yield only repeat candidates; otherwise repeat completions are appended after date candidates (max 6).ScheduledDateField/WhenQueryInputacceptallowRepeat, enabled only on single-entity scheduled-date entry points (task/project context menus, expanded task row, swipe picker, project meta row, keyboard picker with one target). Multi-select, due dates, and quick add do not.{ start: ANYTIME, scheduledDate, repeatRule, reminderTime? }and closes the picker; project entries ignore the time.3. Skip last occurrence ends the repeat
When
untilwas already reached, "Skip Occurrence" was disabled, so "not this time, and stop repeating" had no obvious action.planRepeatSkipnow returns{ patch: taskCancelPatch(now), ends: true }when the chain has no next occurrence, instead ofblocked: 'no-next'.next-existsstill blocks first.ends; the task enters the logbook as cancelled and derivation stops.planProjectRepeatSkipreusesplanProjectComplete(…, 'cancelled')— the project completes, remaining to-dos are cancelled, nothing is derived.useSkipOccurrence/useSkipProjectOccurrenceexposelast; entries are no longer disabled and are relabeled "Skip and End Repeat" with explanatory hints.skipOccurrencedescription and returnedstatusupdated.Testing
normalizeRepeatRule/canonicalRepeatRule(canonical-string lock, ID stability),nextOccurrenceDatemonthDays matrix (intervals, clamping without drift, merging, leap year, completion anchor, until), skip/preview/project instance for monthDays, and last-occurrence skip for tasks and projects.repeat-query.test.tscases (rule table zh/en, clamping, invalid input, completion, parseWhenQuery integration, cross-check against engine).formatRepeatSummaryzh/en, last-occurrence menu entries.All package typecheck, tests (engine, api, ui, backend e2e, desktop, mobile, frontend) and lint pass. Not yet manually verified in the running app.
Notes
monthDaysand derive different dates/IDs; the compatibility window is limited to pre-upgrade clients (ADR-0026) and is accepted.CONTEXT.mdentries for Repeat Rule, Repeat Query and Skip Occurrence updated.