Build TimeHero
KINDAreplaces $5/mosaves $60/yrback to the verdict
A local work planner whose hard problems are computing capacity that really exists, placing tasks by an algorithm you can explain, and refusing to hide the fact that a deadline is going to be missed.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Tauri 2 needs both, and the desktop shell is what makes the keyboard-first behaviour possible.
Get it nodejs.org and rustup.rs, then the Tauri prerequisites for your platform. open ↗
Verify
node --version prints v22 or higher and cargo --version succeeds - have readyfree
Why Capacity computed without your actual meetings is fiction, and the whole build rests on capacity being true.
Get it Most calendars publish a private subscription address; download the file or point the importer at it.
Verify
Imported busy intervals match a week you can check by eye - decidefree
Why The plan is only arguable if the rule is written down, and picking the rule after seeing the output means picking whatever the code already did.
Get it Choose one rule, such as earliest deadline first with a stated tie-break on priority then creation order, and write it into the documentation before implementing it.
Verify
The README names the rule and the tie-break, and the code refers to the same names - decidefree
Why Each is asserted by a test, and stability is a decision rather than an emergent property.
Get it Write down how far ahead placements are pinned, how many minutes of movement is acceptable for a small change, and your working hours with their zone.
Verify
All three are in the configuration and named in tests - have readyfree
Why Infeasibility is only testable against a week that genuinely does not fit, and a made-up one will not.
Get it Take an actual busy week, enter the tasks and estimate them without flattering yourself.
Verify
The planner produces at least one miss with a shortfall you recognise
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- tasks(id, title, estimate_minutes, remaining_minutes, deadline_at, earliest_start_at, priority, project_id, state, created_at) - dependencies(task_id, depends_on_task_id) · a cycle is rejected at write time, not discovered at plan time - hours(weekday, start_local, end_local, timezone) · when you can actually work, as local rules - busy_blocks(id, source, starts_at, ends_at, external_uid) · imported, times only, and tagged with where they came from - plans(id, computed_at, algorithm, params_json, horizon_end, feasible, churn_minutes) - placements(plan_id, task_id, starts_at, ends_at, ordinal, reason) · the reason names the rule that put it there - misses(plan_id, task_id, deadline_at, would_finish_at, shortfall_minutes) · the output that is never hidden - A plan is data with a reason attached to every placement, so 'why is this on Thursday' has an answer that is not a shrug.
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.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_PATH | required | ~/.local/share/planner/plan.db | Tasks, plans and plan history. History is the part you cannot recompute. |
WORK_TIMEZONE | required | Europe/London | Working hours expand from this zone's rules per date. |
PLAN_HORIZON_DAYS | required | 21 | How far ahead the scheduler places work. |
PIN_HORIZON_HOURS | required | 48 | Placements inside this window survive a replan. |
CHURN_LIMIT_MINUTES | required | 120 | A small change moving more than this is a scheduler bug, and the test says so. |
CALENDAR_ICS_URLsecret | optional | https://calendar.example.com/private/basic.ics | Optional. Read-only busy intervals. The app works without it. |
EXPORT_ORIGIN_TAG | required | planner-generated | Marks exported events so the importer can exclude them. |
The build, in order
Capacity that actually exists
Available minutes come from working hours minus real commitments, computed per date.
Not from a fixed offset. Write both transition days as tests first.
Files
src/time/hours.tstests/dst.test.tsTag each one with its source so you can tell yours from theirs.
Files
src/time/capacity.tsA cycle discovered inside the scheduler is a hang with a nice name.
Files
src/tasks/dependencies.tstests/cycle.test.ts
done when · tick each as it passeswatch out- Treating capacity as eight hours a day produces a plan built on time you do not have, and the first thing it does is quietly assume your Tuesday meetings are not happening. Subtract real busy intervals, expand hours from the zone's rules for each date so the two clock change days are right, and show the daily total so a fantasy week is visible before anything is scheduled.
A scheduler you can name
The same inputs produce the same plan, and every placement carries the rule that caused it.
In the code and in the README. An unnamed heuristic drifts.
Files
src/plan/schedule.tsdocs/algorithm.mdThe bug this phase exists to prevent is a day that quietly overfills.
Files
src/plan/schedule.tstests/capacity-invariant.test.tsThe reason is what makes the plan arguable instead of magical.
Files
src/plan/reasons.ts
done when · tick each as it passeswatch out- A greedy pass that sorts by deadline and assigns each task a start time without decrementing the capacity it just consumed puts eleven hours of work into a seven hour Tuesday, and nothing errors because nobody asked whether the day was full. Consume capacity as you place, and assert that the sum of placements in any day never exceeds that day's available minutes.
Saying what will not fit
A deadline that cannot be met is reported with a number, never absorbed.
A test that fails on any write to the field, not a comment.
Files
src/plan/schedule.tstests/no-estimate-writes.test.tsIn minutes. A number is arguable; a warning icon is not.
Files
src/plan/misses.tsAdd these hours, drop this task, or move this date.
Files
src/plan/resolutions.ts
done when · tick each as it passeswatch out- A planner that shaves estimates to make the week fit is worse than a list, because it hands you a schedule that looks achievable, is not, and teaches you within a fortnight to ignore it. Never write to an estimate from the scheduler; produce a miss with a shortfall in minutes and let the person decide what gives.
A plan that does not reshuffle every morning
Replanning moves what it must and reports how much it moved.
Stability beats optimality once a human is following the plan.
Files
src/plan/pinning.tstests/stability.test.tsMinutes moved, against the previous plan.
Files
src/plan/churn.tsWhat changed, and what made it change.
Files
src/views/PlanDiff.tsx
done when · tick each as it passeswatch out- Recomputing the whole plan whenever anything changes rearranges half your week at nine in the morning, and since each rearrangement is individually defensible you cannot argue with it, so by Wednesday you have stopped looking. Pin what has started and the near horizon, replan on triggers, and put a churn number on every plan so a restless scheduler is visible rather than merely annoying.
Getting the plan out without poisoning itproduct builder
The plan exports to a calendar and back without the export becoming input.
So the import can tell your plan from your meetings.
Files
src/export/ics.tstests/roundtrip-capacity.test.tsOtherwise the plan eats its own capacity.
Files
src/import/ics.tsPlan history is the part you cannot recompute.
Files
src/backup/snapshot.tstests/restore.test.ts
done when · tick each as it passeswatch out- Exporting placements to your calendar and then importing that calendar as busy intervals makes the plan block itself: every task appears as a commitment, capacity drops to nothing, and the next plan says the week is impossible. Tag exported events and filter them on import, and test the round trip specifically.
That is the whole plan for TimeHero. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Task creation from email and chat.
- Team planning and workload balancing.
- Time tracking.
- Completion dates presented as promises.
- deep operating-system edge-case coverage
- polished shortcuts and accessibility
- vendor-maintained updates
- cross-device sync
- Estimate calibration from the history you already store, shown as a ratio rather than applied silently
- A second scheduling rule selectable per project, once the first has held its invariants for a month
- Recurring tasks, which are a generator in front of the same scheduler
Need the files? The project pack on the verdict page hands your agent the whole brief · more productivity utilities.