Functional Weave
Code in Python

todo.reschedule-recurrence

When a repeating todo's due date is moved by hand, move its series with it: the new date becomes the anchor.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 14 tests, run in TypeScript, Python and Rust.

What it does

When someone edits a repeating todo and changes its due date, the series moves with it: the new date becomes the recurrence's anchor.

todo.item's `Recurrence` counts every occurrence from its `anchor`, the series' first date, and `validateDraft` refuses a due date before the anchor. So an edit form that simply stored the new date would either be refused (moved earlier) or keep repeating on the old rhythm (moved later). Moving the anchor is what a person means by "move this to Friday".

For example

  • reschedule_recurrence(frequency monthly, interval 1, anchor 2026-09-29, 2026-09-29, 2026-10-05) → frequency monthly, interval 1, anchor 2026-10-05 moving a monthly todo's date moves the series to the new date
  • reschedule_recurrence(frequency weekly, interval 1, anchor 2026-10-01, 2026-10-01, 2026-09-28) → frequency weekly, interval 1, anchor 2026-09-28 moving it earlier than the anchor, which todo.item would otherwise refuse
  • reschedule_recurrence(frequency monthly, interval 1, anchor 2026-01-31, 2026-02-28, 2026-02-28) → frequency monthly, interval 1, anchor 2026-01-31 an unchanged date keeps a month-end anchor after the series rolled forward

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

def reschedule_recurrence(recurrence: Optional[Recurrence], from_due: Optional[str], to_due: Optional[str]) -> Optional[Recurrence]
recurrenceRecurrence?the todo's repeat as stored, or null for a one-off todo
from_duedate?the due date it had
to_duedate?the due date the person gave it
returnsRecurrence?anchored on toDue when the date moved; otherwise unchanged

Your code names it in one line, in the file that uses it

from fune.todo.reschedule_recurrence import reschedule_recurrence  # todo.reschedule-recurrence@^1
impl/python.py · 23 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from dataclasses import replace
from typing import Optional

from .dates_add_days import add_days  ← from dates.add-days ^1.0.0 · built alongside by fune
from .todo_item import Recurrence  ← from todo.item ^1.0.0 · built alongside by fune


def reschedule_recurrence(
    recurrence: Optional[Recurrence], from_due: Optional[str], to_due: Optional[str]
) -> Optional[Recurrence]:
    """The repeat a todo keeps when its due date is edited: a moved date becomes the anchor."""
    # add_days(x, 0) is the date check: it raises dates.add-days's message.
    if from_due is not None:
        add_days(from_due, 0)
    if to_due is not None:
        add_days(to_due, 0)
    if recurrence is None:
        return None
    add_days(recurrence.anchor, 0)
    # No date: todo.item's validate_draft says a repeating todo needs one.
    if to_due is None or to_due == from_due:
        return recurrence
    return replace(recurrence, anchor=to_due)

Install

fune build

With that line in your source, in a Python project (language python in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Python package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add todo.reschedule-recurrence
Download for Python todo.reschedule-recurrence-1.0.0-python.fune · 7,258 bytes sha256 330f30d239519367e0c8ad204635fa789d57ea444deea3a5ce8e720fd7cb87dc

The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./todo.reschedule-recurrence-1.0.0-python.fune, or fetch it from a terminal with fune pull todo.reschedule-recurrence@1.0.0:python.

The whole function, every language, is one file too: todo.reschedule-recurrence-1.0.0.fune, 9,618 bytes, sha256 32bbf5607d31a2d70a36741adcf309620dd29de47922d0b5e4045c5976eb5451. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

# fune: before todo.reschedule-recurrence

after — your function gets the result and the arguments, and returns the final result.

# fune: after todo.reschedule-recurrence

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

# fune: replace dates.add-days in todo.reschedule-recurrence
# fune: replace todo.item in todo.reschedule-recurrence

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show todo.reschedule-recurrence --steps.

# fune: step todo.reschedule-recurrence after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
moving a monthly todo's date moves the series to the new date frequency monthly, interval 1, anchor 2026-09-29, 2026-09-29, 2026-10-05 → frequency monthly, interval 1, anchor 2026-10-05
moving it earlier than the anchor, which todo.item would otherwise refuse frequency weekly, interval 1, anchor 2026-10-01, 2026-10-01, 2026-09-28 → frequency weekly, interval 1, anchor 2026-09-28
an unchanged date keeps a month-end anchor after the series rolled forward frequency monthly, interval 1, anchor 2026-01-31, 2026-02-28, 2026-02-28 → frequency monthly, interval 1, anchor 2026-01-31
a rolled-forward series moved by two days is anchored on the new date, not the old anchor frequency monthly, interval 1, anchor 2026-01-31, 2026-02-28, 2026-03-02 → frequency monthly, interval 1, anchor 2026-03-02
frequency and interval are kept frequency weekly, interval 2, anchor 2026-09-28, 2026-09-28, 2026-10-02 → frequency weekly, interval 2, anchor 2026-10-02
a one-off todo stays one-off —, 2026-09-28, 2026-10-01 → —
clearing the date leaves the repeat for todo.item to report frequency daily, interval 1, anchor 2026-09-28, 2026-09-28, — → frequency daily, interval 1, anchor 2026-09-28
a repeat that had no date gets one: the new date is the anchor frequency daily, interval 1, anchor 2026-09-28, —, 2026-10-01 → frequency daily, interval 1, anchor 2026-10-01
a weekday series moved to a Saturday is anchored there as given frequency weekdays, interval 1, anchor 2026-09-28, 2026-09-30, 2026-10-03 → frequency weekdays, interval 1, anchor 2026-10-03
no dates at all and no repeat —, —, — → —
Show the other 4 tests
CaseArgumentsExpected
an impossible new date is an error frequency daily, interval 1, anchor 2026-09-28, 2026-09-28, 2026-02-30 → error: is not a real calendar date
a malformed old date is an error frequency daily, interval 1, anchor 2026-09-28, 28/09/2026, 2026-10-01 → error: is not an ISO date
a malformed anchor is an error frequency monthly, interval 1, anchor 2026-13-01, 2026-09-28, 2026-10-01 → error: is not a real calendar date
a bad date is an error even for a one-off todo —, 2026-09-28, 2026-9-30 → error: is not an ISO date

More from the author

| recurrence | fromDue → toDue | result | | --- | --- | --- | | null | anything | null | | any | toDue null | unchanged (validateDraft then says a repeating todo needs a due date) | | any | toDue = fromDue | unchanged | | any | toDue ≠ fromDue | the same frequency and interval, anchored on toDue |

- **An unchanged date keeps the anchor.** A monthly series from 31 January is due 28 February after one roll forward; saving an edit to its title must not re-anchor it on the 28th, or it would lose the month end. - A new date is taken as given, even a Saturday for a weekday series: todo.next-occurrence decides the following dates from there. - Every date given (`fromDue`, `toDue`, the anchor) must be a real ISO date, with or without a recurrence; the messages are dates.add-days's (`"2026-02-30" is not a real calendar date`).

Files

PathBytes
README.md1,367
impl/python.py833
impl/rust.rs1,257
impl/typescript.ts1,015
vectors.json2,954