Build SkedPal

KINDAreplaces $9.95/mosaves $119.4/yrback to the verdict

0%0 of 17 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.

An automatic scheduler whose hard problems are reading a calendar as busy time without learning what is in it, producing a plan that is stable and explainable rather than freshly shuffled every hour, and writing blocks back without ever touching an event it did not create.

estimated effort multi-daythe files for this build are in the project pack

RuntimeTauri 2 with React and TypeScript, SQLite beneathCalendarFree and busy intervals only, through the provider's own busy querySolverA deterministic pass over an explicit priority order with a recorded tie-breakTimeInstants stored, rules written in a named zone, expanded per dateWrite-backOnly events the tool created, tagged and tracked by their stored identifier

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 Builds the interface.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why Builds the desktop shell that holds the task list locally.

    Get it rustup.rs, then the Tauri prerequisites for your platform. open ↗

    Verify cargo tauri info reports no missing dependencies

  3. free

    Why Writing into a dedicated calendar keeps every mistake visible and reversible, which matters more here than anywhere else in the build.

    Get it Create an empty calendar in your account and register an application with the narrowest scope that reads busy time and writes events. open ↗

    Verify The busy query returns intervals and a test event can be created in the new calendar

  4. decidefree

    Why Both transitions are asserted in the suite, and deciding after seeing failures means choosing whatever passes.

    Get it Write down what a rule landing in the missing hour does and what happens on the repeated one.

    Verify Both are named in the README and in the tests

  5. have readyfree

    Why Determinism and stability only look like features against a messy real week; a tidy fixture hides both.

    Get it Put your actual flexible work into the tool with honest durations and due dates.

    Verify A plan over the real week reports its shortfall rather than fitting suspiciously well

Data model

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

- tasks(id, title, total_minutes, remaining_minutes, due_at, earliest_at, priority, min_chunk_minutes, max_chunk_minutes, time_map_id, state)
- time_maps(id, name, timezone, rules_json) · when a kind of work is allowed, written in the map's own zone
- busy(id, calendar_id, starts_at, ends_at, external_id, is_ours) · start and end and nothing else, because nothing else is fetched
- plans(id, computed_at, horizon_days, inputs_sha256, solver_version, unplaceable_json) · the hash makes an identical rerun provable
- blocks(id, plan_id, task_id, starts_at, ends_at, placed_by_rule, state, external_event_id, pinned_at)
- calendars(id, provider, account_ref, scopes_json, last_synced_at, revoked_at)
- The busy table has no title column, so there is nothing for the scheduler, a log or a crash report to leak.

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/scheduler/tasks.dbTasks, time maps, plans and busy intervals.
CALENDAR_CLIENT_IDrequiredyour-application-idThe registered application with the narrow scope.
CALENDAR_CLIENT_SECRETsecretrequiredredactedStored in the OS keychain where available, never in the database.
WRITE_CALENDAR_IDrequiredscheduler@group.calendar.google.comThe dedicated calendar. The tool never writes anywhere else.
PLANNING_HORIZON_DAYSrequired14How far ahead to plan. Longer horizons make the capacity figure more honest and the plan less stable.
STALE_SYNC_MINUTESrequired30Older than this and availability narrows rather than assuming free time.
SOLVER_VERSIONrequired1Recorded on every plan, so a plan change caused by a code change is distinguishable from one caused by your week.

The build, in order

  1. Busy time, and not one word more

    The calendar is read as intervals with no event content anywhere in the system.

    1. The endpoint that returns less is the endpoint that leaks less.

      Files src/calendar/freebusy.ts

    2. The per-request calendar cap is published and it is not large.

      Files src/calendar/freebusy.tstests/paging.test.ts

    3. A schema test is the only version of this promise that stays true.

      Files tests/no-event-content.test.ts

    done when · tick each as it passes
    watch out
    • Reading the full event list because it is the easier endpoint puts titles, attendees and locations into your database, your logs and eventually a crash report, and none of that is needed to know that three o'clock is taken. Use the busy query, and assert in the suite that no title, attendee or location column exists anywhere.
  2. Time maps across a clock change

    Available slots are computed from rules in a named zone and are right on the two awkward days.

    1. Never by arithmetic on a local midnight.

      Files src/schedule/expand.tstests/dst.test.ts

    2. A partial overlap is the case that hand-rolled interval code gets wrong.

      Files src/schedule/slots.tstests/overlap.test.ts

    3. Travelling should not silently rewrite when you work.

      Files src/schedule/timemap.ts

    done when · tick each as it passes
    watch out
    • Generating slots by adding minutes to a local midnight offers a nine o'clock slot that does not exist on the spring transition and two of them on the autumn one, which is rare enough to pass every test written in June and common enough to wreck a week twice a year. Expand from the zone's rules for that specific date, and put both transition days in the suite.
  3. A plan that is the same plan tomorrow

    The solver is deterministic, records why each block landed where it did, and does not reshuffle work already begun.

    1. Including an identifier at the end so nothing is ever a real tie.

      Files src/solver/order.tstests/determinism.test.ts

    2. It turns 'nothing changed' from a feeling into a comparison.

      Files src/solver/plan.ts

    3. A scheduler that moves what you already began is a scheduler you stop opening.

      Files src/solver/pins.tstests/stability.test.ts

    done when · tick each as it passes
    watch out
    • A solver that replans from scratch every hour moves this afternoon's work to Thursday because a new task arrived, and after the third time nobody believes the calendar; worse, an unstable tie-break makes two runs over identical inputs disagree, so the person cannot tell a real change from noise. Order deterministically, hash the inputs, and hold pinned and started blocks still.
  4. Writing blocks back without stomping

    The calendar gains the tool's blocks and loses nothing else.

    1. It makes the blast radius of any mistake visible and small.

      Files src/calendar/write.ts

    2. The range delete is the mistake that removes a real appointment.

      Files src/calendar/write.tstests/never-touch-foreign.test.ts

    3. A person moving a block is giving you information, not making an error.

      Files src/calendar/reconcile.ts

    done when · tick each as it passes
    watch out
    • Clearing the tool's blocks by deleting every event in a time range removes the dentist appointment that happened to fall inside it, and there is no undo worth the name. Delete only by stored identifier, and if an identifier is missing, leave the event alone and report it.
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 productivity utilities.