Build Any.do

YESreplaces $7.99/mosaves $95.88/yrback to the verdict

0%0 of 16 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A desktop task manager whose hard problems are storing scheduled times so they still mean the same thing after a government changes the clocks, firing reminders exactly once across sleep and restarts, and letting a recurring series be edited without rewriting what already happened.

estimated effort weekendthe files for this build are in the project pack

RuntimeA desktop application with a global capture shortcut and native notificationsStorageSQLite beside the application, one file, backed up on a scheduleTimeWall clock plus an IANA zone identifier, with instants derived at scheduling timeRecurrenceA rule on the series, occurrences computed on read, rows written only when something happensDeliveryA scheduler that records each fire and refuses to fire the same occurrence twice

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the application and the test suite.

    Get it nodejs.org, or your version manager. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why Both capture and delivery depend on working while the window is closed, which a web page cannot do dependably.

    Get it Any desktop shell for your stack that exposes a global shortcut and the system notification service.

    Verify A shortcut pressed in another application opens capture, and a notification appears with the window closed

  3. installfree

    Why The whole scheduling design depends on knowing which database version produced an instant, so a library that hides the version is unusable here.

    Get it Use your runtime's internationalisation support or a maintained library that exposes the version. open ↗

    Verify The application prints the database version at startup and a test can stub it

  4. decidefree

    Why Twice a year a clock change makes some wall clock times impossible and others ambiguous, and picking a rule in the debugger later means picking whatever the library happened to do.

    Get it Decide and write down both rules before implementing the scheduler.

    Verify docs/time-rules.md states both and each has a test

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- tasks(id, title, notes, project_id, priority, starts_on, due_on, created_at, completed_at, deleted_at)
- schedules(task_id, rule_text, local_time, zone_id, starts_on, ends_on) · local_time and zone_id together, never a stored instant for a future occurrence
- occurrences(task_id, occurrence_date, state, completed_at, skipped_at, overridden_json) · a row exists only once something happened to that date
- reminders(id, task_id, occurrence_date, fire_at_utc, tzdb_version, state, fired_at) · the instant is derived at scheduling time and recorded with the database version that produced it
- projects(id, name, position) and tags(task_id, tag) · the two groupings that earn their keep
- audit(id, entity, entity_id, action, at, payload_json) · what undo reads, and what tells you where a task went
- The only UTC instants here are on reminders inside the scheduling horizon, and every one of them records which time zone database version computed it.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired~/.local/share/tasks/tasks.dbEverything. This is the file to back up.
BACKUP_DIRrequired~/.local/share/tasks/backupsAutomatic copies. Restore is tested against these.
BACKUP_KEEPrequired14How many to retain. A task database is small, so keep plenty.
SCHEDULING_HORIZON_HOURSrequired48How far ahead reminders are converted into instants. Beyond this, only wall clock and zone exist.
CATCHUP_GRACE_MINUTESrequired30Misses newer than this fire normally; older ones collapse into one summary.
TODAY_VIEW_LIMITrequired12The day view stops here. Overdue is a count with its own screen.

The build, in order

  1. Capture before you lose the thought

    A task goes in with one keystroke from anywhere, and nothing typed is ever lost.

    1. The thought arrives while you are doing something else.

      Files src/capture/window.ts

    2. Guessing silently is worse than not guessing.

      Files src/capture/parse.tstests/parse.test.ts

    3. Undo built on an audit trail also answers where a task went.

      Files src/store/audit.ts

    done when · tick each as it passes
    watch out
    • A capture box that parses 'friday' out of the title and silently rewrites what you typed means the day you write 'call about friday delivery' the title is mangled and the task is due at the wrong time. Show the parsed date as a separate chip you can reject, and never remove text from the title without the user seeing it happen.
  2. Time stored the way a person meant it

    A scheduled time is a wall clock time in a named zone, and instants are derived rather than stored.

    1. The rules for that instant have not all been made yet.

      Files src/time/schedule.tstests/zone.test.ts

    2. It is how you know which reminders need recomputing.

      Files src/time/derive.ts

    3. Both happen twice a year and both need a decision, not an exception.

      Files docs/time-rules.mdtests/dst.test.ts

    done when · tick each as it passes
    watch out
    • Storing 'weekdays at 08:00' as a UTC instant bakes in the offset rules that happened to exist on the day you saved it, and those rules change: release 2026d of the time zone database, published on 11 September 2026, recorded that Canada's Northwest Territories moved to permanent minus six from 21 August 2026. Every future reminder stored in that zone as an instant was then wrong by an hour, and nobody finds out until somebody is late. Store the wall clock and the zone, derive the instant late, and recompute when the database version changes.
  3. Reminders that fire once

    Notifications survive sleep, restarts and a closed window, and never arrive in a burst.

    1. A crash between the two should lose a notification, not repeat it forever.

      Files src/reminders/deliver.ts

    2. Forty notifications is the same as none.

      Files src/reminders/catchup.tstests/catchup.test.ts

    3. Otherwise a few thousand tasks become a few thousand timers.

      Files src/reminders/scheduler.ts

    done when · tick each as it passes
    watch out
    • Computing missed reminders after a laptop has been shut for three days and firing them all produces forty notifications in one second, which the person dismisses in one gesture, losing the two that mattered along with the thirty eight that did not. Fire anything inside the grace period normally, collapse everything older into a single summary, and mark them all delivered.
  4. A series, its occurrences, and its history

    Completing, skipping or editing one occurrence never rewrites the rest of the series or the past.

    1. Ten years of a daily task is a computation, not ten years of rows.

      Files src/recurrence/expand.tstests/expand.test.ts

    2. The past was kept under the old rule and should stay that way.

      Files src/recurrence/split.ts

    3. A skipped day is information, not a gap.

      Files src/recurrence/state.ts

    done when · tick each as it passes
    watch out
    • Implementing 'edit this and following' by changing the rule on the existing series silently rewrites every completed occurrence's expected date, and the history of a habit you have kept for a year becomes wrong in a way you cannot reconstruct. End the old schedule on the day before the change and create a new one, so the past keeps the rule it was actually kept under.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more tasks.