Build Capacities

YESreplaces $11.99/mosaves $143.88/yrback to the verdict

0%0 of 19 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 notes application built on typed objects with defined properties, where mentioning a person creates a real entity rather than a link to nothing, and where the daily note is the capture surface that feeds everything else without you filing anything.

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

RuntimeTypeScript with a local application and SQLiteObjectsTypes with declared properties, stored relationallyEditorBlock-based, with inline object referencesSearchFull text plus property filtersExportMarkdown with front matter, always available

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.

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

    Verify node --version prints v22 or higher

  2. decidefree

    Why Types shape everything and are painful to change once you have hundreds of objects. Four is enough to test the model and few enough to change your mind.

    Get it Look at what you already write about. Person, book, idea and meeting is a reasonable start if nothing obvious presents itself.

    Verify Four types are written down with their properties before any code

  3. have readyfree

    Why Search performance, schema migration and export all behave differently at scale, and a knowledge base with twenty notes proves nothing.

    Get it Export from whatever you use now, or generate a realistic corpus with varied references.

    Verify Several hundred objects with references exist to test against

  4. have readypennies per month at personal scale

    Why Phase 6 only. Sync goes through storage you own rather than a service that owns your notes.

    Get it Any object storage or a synced folder. The requirement is that you control it.

    Verify Two machines can read and write the same location

Data model

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

- types(id, name, icon, property_schema_json) · person, book, idea, meeting, whatever you define
- objects(id, type_id, title, created_at, updated_at) · every note is an object of some type
- properties(object_id, key, value_json) · typed per the type's schema, validated on write
- blocks(id, object_id, ordinal, kind, text, children_json) · the body content
- references(from_object_id, from_block_id, to_object_id, kind) · kind is mention or property_link
- daily(date, object_id) · the daily note is an object too, which is why capture and structure are the same system
- A reference is a row, so backlinks are a query rather than a text search across every note.

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/capacities/base.dbYour entire knowledge base. The single most important file to back up.
EXPORT_DIRrequired~/Documents/notes-exportWhere Markdown exports are written. Somewhere your ordinary backups already reach.
DAILY_NOTE_START_HOURrequired4When a new day starts, so a note written at one in the morning lands on the right day.
SYNC_REMOTEoptionals3://my-notes/basePhase 6 only. Absent means single machine, which is a fine way to run.
BACKUP_DIRrequired~/backups/capacitiesAutomated backups. Restore-tested, or they do not count.

The build, in order

  1. Typed objects with real properties

    A person has a role and a book has an author, enforced.

    1. Kind, required, default. This is what separates this from a folder of pages.

      Files src/types.ts

    2. A pure function against the schema, unit tested with wrong kinds.

      Files src/validate.tssrc/validate.test.ts

    3. Existing objects keep data and are marked incomplete. Decide this now, not at three hundred objects.

      Files src/migrate.ts

    done when · tick each as it passes
    watch out
    • Adding a required property to a type with three hundred existing objects either fails, silently invalidates all of them, or quietly drops the constraint. Decide the migration behaviour before you need it: existing objects keep their data and are marked incomplete rather than being rejected or rewritten.
  2. Mentions that create the thing mentioned

    Typing a person's name in a note produces a person object, not a dead link.

    1. So renaming is free. Text references are the mistake that shows up months in.

      Files src/reference.ts

    2. Choosing the type at mention time is what keeps the graph typed rather than generic.

      Files src/mention.ts

    3. Refuse or convert with a warning. Never leave dangling references.

      Files src/delete.ts

    done when · tick each as it passes
    watch out
    • References stored as the displayed text mean renaming an object breaks every mention of it, and the breakage is silent because nothing checks. Store the id and render the current title, so renaming is free and a reference can never dangle.
  3. The daily note as the capture surface

    You write into today and the structure builds itself.

    1. So it participates in references like anything else.

      Files src/daily.ts

    2. No inbox, no later step. This is the design that survives contact with real use.

      Files src/daily.ts

    3. A person's page becomes a timeline of when you mentioned them.

      Files src/backlinks.ts

    done when · tick each as it passes
    watch out
    • A capture surface that requires you to file things later accumulates an unfiled backlog and stops being used within a month. Make the mention itself the filing, so writing a person's name in today's note is already the connection and nothing needs to be moved.
  4. Finding things by what they are

    Every book you rated above four, in one query.

    1. Full text search over content, scoped by object type.

      Files src/search.ts

    2. The reason the property model exists. Filters over typed values, not string matching.

      Files src/query.ts

    3. So a view is part of the knowledge base rather than application state.

      Files src/saved.ts

    done when · tick each as it passes
    watch out
    • Full text search alone cannot answer the questions typed objects exist to make answerable, and building only text search wastes the entire property model. The filter path is the reason for the schema and needs to be there from the start rather than added once the data is large.
  5. Getting everything out

    Your knowledge base leaves in a form you could read in ten years.

    1. Properties in the front matter, body as Markdown. Readable without your application.

      Files src/export.ts

    2. Export, import, compare. A backup untested is not a backup.

      Files tests/roundtrip.test.ts

    3. Ten thousand objects should not need ten thousand objects of memory.

      Files src/export.ts

    done when · tick each as it passes
    watch out
    • An export that loses references or property types is a backup you cannot actually restore from, and you discover that at the moment you need it. Test the round trip in the suite, comparing objects, properties and references after re-import.
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 notes & knowledge.