Build Akiflow

KINDAreplaces $34/mosaves $408/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.

A planner that pulls tasks out of the places they arrive and puts them into one queue you empty, where the engineering is keeping several external systems in sync without duplicating or losing anything, and the design is that every action must be reachable without a mouse.

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

SourcesRead-write connectors per source with stable external identitySyncTwo-way with conflict detection, never last write winsCommand barEvery action reachable by keyboard from anywherePlanningTasks placed onto calendar time blocksRuntimeTypeScript with a local application and SQLite

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 local application and the sync workers.

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

    Verify node --version prints v22 or higher

  2. free with existing subscriptions

    Why Two-way sync behaviour, identifier stability and conflict cases differ per system and none of it can be assumed from documentation.

    Get it Accounts on two systems you actually use, with write scopes, and read their sync and identifier documentation before building.

    Verify A task created here appears there and a change there appears here

  3. free

    Why Time blocks are real calendar events, and the failure modes only appear against a real calendar with existing meetings.

    Get it A calendar with a month of realistic meetings including recurring series.

    Verify Events can be created and the tool's own events are identifiable by stored identifier

  4. have readyfree

    Why Triage speed and inbox behaviour are only meaningful against a real backlog with the awkward items you have been avoiding.

    Get it Import everything from the systems you actually use, including the fifty things you have been ignoring.

    Verify At least two hundred tasks are imported across two sources

  5. decidefree

    Why Every action is keyboard reachable and retrofitting a scheme after building the interface means rebuilding the interface.

    Get it Decide the command bar key, the triage keys and the navigation keys before building phase three.

    Verify A written scheme covers every planned triage action

Data model

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

- sources(id, kind, account_ref, connected_at, last_synced_at, cursor, disabled_reason)
- tasks(id, source_id, external_id, title, notes, due_at, completed_at, deleted_at, updated_at, local_version, remote_version)
- inbox(task_id, arrived_at, triaged_at) · the queue you empty
- blocks(id, task_id, starts_at, ends_at, calendar_event_id, pinned) · time on the calendar
- conflicts(id, task_id, field, local_value, remote_value, detected_at, resolved_at, resolution)
- commands(id, name, keys, context) · the command surface, so every action is reachable
- A task holds both a local and a remote version, because detecting that both changed is the only way to avoid silently discarding one.

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
DB_PATHrequired~/.local/share/planner/tasks.dbTasks, conflicts and the offline queue.
SOURCE_TOKENSsecretrequireda keychain referenceWrite access to your task systems. Scope narrowly.
CALENDAR_CLIENT_IDsecretrequireda provider client idCreate and update only the tool's own events.
SYNC_STALE_HOURSrequired12When a source is surfaced as stale. Silence is the failure mode.
CONFLICT_RESOLUTIONrequiredaskNever last write wins. The rare case is worth a question.
COMMAND_BAR_KEYrequiredCtrl+KThe entry point to every action. Choose something nothing else claims.

The build, in order

  1. One task, however many systems

    A task from any source is one item here and stays linked to its origin.

    1. Titles collide and change. Identifiers do not.

      Files src/sources.ts

    2. Some systems reuse identifiers, which is worse than changing them.

      Files src/sync.ts

    3. A disconnected source is not a deletion instruction.

      Files src/sources.ts

    done when · tick each as it passes
    watch out
    • Deduplicating imported tasks on title means two genuinely different tasks called follow up merge into one and a note is lost, while the same task retitled at the source arrives as a second item. Key on the source's own identifiers and treat a missing identifier as a source you cannot sync rather than one to guess about.
  2. Writing back without losing anything

    A change made in both places is detected rather than silently resolved.

    1. It is the only way to know both changed.

      Files src/versions.ts

    2. They are rare enough that asking is tolerable and losing a note is not.

      Files src/conflicts.tstests/conflict.test.ts

    3. A retried completion should not complete twice.

      Files src/write.ts

    done when · tick each as it passes
    watch out
    • Last write wins across a two-way sync silently discards whichever change arrived first, and for a task that is usually a note somebody typed and will never think to check again. Track both versions, detect divergence, and ask, since the cases are rare enough that asking is tolerable.
  3. An inbox you can actually empty

    Triaging fifty items takes minutes and never a mouse.

    1. One mouse reach per item breaks the rhythm and the model.

      Files src/commands.ts

    2. So actions are discoverable without memorising them.

      Files src/command-bar.ts

    3. Fast triage produces mistakes and they should be cheap.

      Files src/undo.ts

    done when · tick each as it passes
    watch out
    • A triage flow requiring a mouse for any step breaks the rhythm entirely, and a rhythm broken every few items means the inbox does not get emptied and the whole model collapses. Make every action keyboard reachable and measure a realistic triage session rather than assuming.
  4. Tasks on the calendar, honestly

    Planned work occupies real time and the calendar reflects it.

    1. Never by title. A rename must not orphan a block.

      Files src/calendar.ts

    2. Tested by attempting it and expecting a refusal.

      Files tests/foreign-events.test.ts

    3. Planning eleven hours into eight is worth being told about.

      Files src/capacity.ts

    done when · tick each as it passes
    watch out
    • Identifying the tool's own calendar events by title or colour means a renamed block becomes invisible to the tool and gets duplicated, or worse, a coincidence of naming makes it modify a real meeting. Store the created event identifier and treat everything else as untouchable.
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 & calendar.