The capture model is how values are put into a Trackable: the editors that take a value and the trackable’s capture config, the capture JSON column (CaptureConfig in packages/core/src/value-schema.ts). The config holds quick-add amounts, the starting value, computed fills, what an empty day means and where the trackable is asked by default. None of it changes a stored value, so all of it is safe to edit at any time. How a day of entries becomes one value is a separate question, its Reading.

Every log appends

Every log writes a new Entry. There is no one-a-day rule and no logging mode to choose, so logging again never edits an earlier entry, and a double tap is two entries. One a day is not offered as a setting, since nothing needs it: a Reading gives the day its one value, a card’s answer for a day opens its entries to edit, and Undo takes back a double tap.

A day reads through the trackable’s Reading: Water’s day is the Sum of its entries, Weight’s the Last, and a scale’s the Last rather than the Mean, since a mean of a scale’s codes falls between its labels. A log offers Undo in a toast, and Add a note where the trackable takes one, which opens the entry’s editor on its note (useLogEntry). A card’s answer for a day opens that day’s entries to edit.

A trackable logged with no value (a pill taken, Journal) is a moment: each entry is only when it happened, read by default as its time of day under Last (Time of Day). The Daily habits module reads its moments as Any, “did I”, with an empty day meaning none.

The editor

One shared editor, ValueEditor (apps/renderer/src/components/capture/ValueEditor.tsx), turns a trackable’s own value into an input, chosen by its kind rather than opted into:

own valueinput
a number− and + by its Step around a field, with its quick-add chips; a slider between its Limits when it is set to one, has both Limits and has no measure (slidesOn)
options in ordera slider over the options
options not in order, a listchips, and a field for a new word where the list takes one
a yes/noits two words, Yes and No unless others are set (Presentation Layer)
a texta field

Every surface that logs a value hosts that editor through the entry panel (Entry Panel): the quick-add palette, the log dialog, a Home card, a day’s list, the Rundown; the entries table edits a cell with it. An interval, a record, an item and a moment are not one value, and the entry panel gives each its own form.

A value, not a change. You log the entry’s value; the day’s total is read from the entries, not a counter you nudge. A number’s quick-add chips set the draft to their amount.

Stage, then log. Picking a value writes nothing; only Log does. The editor decides whether there is anything to log: a new number needs a value other than 0 (an add can take away), any other new entry needs a pick, and an edit needs a value that differs from what is stored. A value outside the limits cannot be logged, and the editor says why under it.

Quick-add amounts and the starting value

Quick-add amounts (capture.quick_add, the form’s Quick-add amounts for a number) are one-tap amounts: Water’s 1, 2 and 8 glasses. The first is also what a new entry starts at.

The starting value of a new number entry (numberSeed in apps/renderer/src/lib/series.ts) is the configured constant (default_value: { strategy: "constant" }, Water’s 1), else the first quick-add amount, else the Step, else 1. It is never today’s total, because adding to a running total would count it twice. An edit starts from the stored value.

Prefill is a checkbox for any trackable with a value to start (takesPrefill), stored as default_value: { strategy: "last-entry" }, which opens a new entry on the last value it held (Frictionless Capture has each kind), ordered by when it happened (lastKnownValue, so a backfilled old weigh-in does not become the start; see Entry on the two kinds of latest). A configured constant comes before it; a surface that passes no history, like the entries table’s cell, which commits on blur, starts from the ordinary default. The strategies are constant, last-entry, min and empty.

Limits

A number’s Limits (min, max) and Step live on its field (Value Types); its Step is how far − and + move it. Both Limits are enforced and include their ends. One rule, numberProblem in packages/core/src/value-schema.ts, says what is wrong with a number: under its lowest, over its highest, or a fraction where only whole numbers belong. The editor applies it in the unit on screen and says why under the value; the entry repository applies it again at the write boundary, in the unit the value is stored in (entryProblem in packages/core/src/entry-check.ts), as the backstop for every other way in.

Limits are kept in the unit they were typed in (limits_unit), as a zone’s bounds are (Zones), so changing the unit preference from kilograms to pounds does not move them. Limits stored without that unit are read in the display unit by the editor, and the repository does not check them rather than guess a unit.

A record’s number fields are held to the same rule, wholeness included: Set’s Reps are whole and at least 1. The record form refuses with the editor’s sentences (“Reps is at least 1.”) and turns off the browser’s own validation so those are the ones you see. Wholeness is checked where a value is stored only for a number with no measure (wholeWhereStored): 2 lb is whole and its grams are not, so the value editor and the record form (fieldsProblem in apps/renderer/src/components/entries/EntryFields.tsx) both check a measured whole number in the unit on screen: 135 lb passes, and 2.5 kg does not.

Computed fills

A record trackable can declare computed fills (capture.fills): when a reference field is picked, a formula fills another field, rounded and still editable. Each is { on, set, expr, bindings, round }. A binding is a number from one of two places: refAttribute, an attribute of the item just picked, read from its resolved attributes so a value lent through Based on works as a stored one does; or latestTrackable, the latest value of another trackable by id or by dimension, ordered by when it happened. expr folds the bindings (Derived Trackables’ per-entry arithmetic). If a binding has no value the fill is skipped: an exercise with no bodyweight_factor has none, which is not 0. A latest value is converted from its base unit to the dimension’s own display unit, the unit a record field holds.

Bodyweight-relative loads are a fill: Set’s weight is bodyweight_factor × the latest mass, declared in the module that makes the trackable rather than written into the form. The resolver is resolveFillSource in apps/renderer/src/components/entries/fill.ts.

Whole day

Whole day, a glossary term, is a setting in the Cadence section of the trackable form, offered for any trackable without an interval, which has times of its own. It sets time_granularity: "date" (wholeDay in packages/core/src/clock.ts). Every write of an entry to such a trackable keeps happened_at as the day alone, in the repository, so no surface can forget it. The log panel opens on the day’s entry when there is one, with Save changes; it shows the day and no time, and leaves out a moment’s large time. Wherever an entry’s time is shown, one kept by its day alone reads Whole day (clockText), never its date. No Timeline is offered for it. It can log more than once a day like any trackable, and Readings, the Rundown and sync read its entries as any other. A moment kept by its day alone has no time of day, so a time-of-day Reading leaves it out while Any and Count read it (Time of Day).

Where it is asked

capture.check_in lists the places in the day a trackable is asked by default, one row each: First thing, Last thing, a time, or any time, with a label where it is asked more than once and, for an interval, which end a row asks. Empty is not asked; absent means a yes/no, a choice or a list is asked once, any time, and anything else is not (checkInPlaces in packages/core/src/check-in.ts).

Where a trackable is actually asked on a date is the Rundown’s, in its layers. The trackable’s own places are what it joins Every day with, when Every day is first made or its module added (joinRundown), and nothing else: the span a Nothing covers is the day’s Rundown’s too (Entry). Only modules write them. The form does not: its box adds a new trackable to Every day, any time, and its Cadence section shows the trackable’s place in the Rundown’s layers. The Rundown page’s Place by usual time writes the layers too. A place’s time is set, never worked out live; usualTimeOfDay only suggests one (Time of Day).

What an empty day means

Per trackable (capture.absence), a day with no entry is either unknown, the default, or means none. The form offers it as “A day with nothing logged”: Isn’t answered, or Means none.

  • Isn’t answered: a day is answered only by an entry, a Nothing already logged included (Entry). A day it is asked and left unanswered can be made an Issue from the day the setting is turned on (capture.issue_unanswered_since, “Left unanswered is an issue”).
  • Means none: every day from the first entry to today on which anything at all was logged, and which is not on hold, is answered, so a count, a sum or a yes/no reads 0 there. A day you logged nothing anywhere stays unknown, so a day away is not a day of none.

Means none suits a trackable logged on every day it happens, such as Journal. It does not suit Diet or Alcohol: a day you used the app says nothing about whether you paid attention to what you ate, so under Means none a day you logged only your mood would read as a day you ate nothing.

Other settings

The Cadence section also carries two settings that change what a trackable’s entries do rather than how they are logged: its entries put the days they touch on hold (capture.holds, Disruptions), and its entries hold the entries done during them (capture.routine, Events). A trackable logged for you from a source carries it in capture.source (Weather from NASA POWER).

Trackable · Entry · Value Types · Presentation Layer · Entry Panel · Frictionless Capture · Readings