Commands
The one registry of verbs every surface shares — what each takes, where it can run, and what it is allowed to change.
A command is one thing the app can do, written the way you would write a function call:
{ "command": "incrementValue", "property": "reps", "by": 1 }
There is exactly one list of them, and every surface invokes from it: a card, a dashboard board, an automation. Cards used to have their own verbs, boards had theirs and automations had a third set; they are one vocabulary now, which is why an automation and a button can do the same thing without you learning it twice.
A handler is a flat list
An event maps to an array of commands, run in order:
"on": { "click": [
{ "command": "setValue", "property": "logged", "value": true },
{ "command": "effect", "name": "celebrate" }
] }
There is no if, no loop and no variable in that array, and that is deliberate rather than unfinished. Conditionality lives one level up, in rules and in expressions — a rule decides whether an element shows, or what it looks like, and the handler underneath it just does its job. Handlers stay short on purpose; the list is a script, not a program.
What a command may target
Each command declares the kind of property it can point at, and the app checks that structurally — by what the property actually is, never by what a Collection happens to be called.
toggleValuetargets a checkbox.incrementValuetargets a number.startTimertargets a stopwatch,recordRoutetargets a route.setValuetargets anything you can write to.
Aim one at the wrong kind of property and it simply does not render. No error dialog, no dead button, no sheet that opens empty — the affordance is just not offered. So a card that a model wrote against a Collection you have since changed degrades quietly instead of breaking.
Arguments are typed and bounded the same way: an id has to look like an id, a date like a date, a number has to sit inside its range, and a word from a fixed list has to be on the list.
Writing is a normal, revertible thing
Some commands change your data — the Writes column below says which. There is nothing special about a write a document causes: it goes down the same path as an edit you make by hand, so it appears instantly, syncs in the background, and can be undone. A document cannot cause a change the app does not already know how to reverse.
That is also why decoration can act. A tappable icon is not a second, sneakier way to write — it is a command invocation like any other, and it is recorded like any other.
Surfaces
Every command lists the surfaces it may be used on:
- card — a card or a list row for one entry
- board — a dashboard widget or a canvas board
- automation — a rule that runs on its own
A command used on the wrong surface is rejected when the document is saved, with a message naming the surfaces it does allow.
The commands
19 commands, generated straight from the registry the app runs — if it is listed here, it works.
| Command | Arguments | Surfaces | Target | Writes | What it does | Example |
|---|---|---|---|---|---|---|
setValue |
| card, board, automation | any property you can write to | Yes | Writes a bounded literal into a property. | {"command":"setValue","property":"stage_3","value":"done"} |
setValueNow |
| automation | date or datetime | Yes | Stamps a date or datetime property with the moment it ran — the clock supplies the value. | {"command":"setValueNow","property":"completed_at_4"} |
clearValue |
| automation | any property you can write to | Yes | Empties a property, whatever its kind. | {"command":"clearValue","property":"blocked_by_7"} |
toggleValue |
| card, board, automation | boolean | Yes | Flips a checkbox — anything not already ticked becomes ticked. | {"command":"toggleValue","property":"done_1"} |
incrementValue |
| card, board, automation | number | Yes | Adds `by` (default 1, negative counts down) to a number property, reading an unset one as zero. | {"command":"incrementValue","property":"reps_2","by":1} |
addTag |
| automation | tag or multi_tag or global_tag or global_multi_tag | Yes | Adds one tag by its id, leaving the property's other tags in place. | {"command":"addTag","property":"tags_5","tag":"shipped"} |
removeTag |
| automation | tag or multi_tag or global_tag or global_multi_tag | Yes | Removes one tag by its id, leaving the property's other tags in place. | {"command":"removeTag","property":"tags_5","tag":"blocked"} |
editValue |
| card | any property you can write to | Yes | Focuses the cell's own inline editor so the value can be typed in place. | {"command":"editValue","property":"notes_3"} |
openEntry |
| card, board | — | No | Opens an entry's own full page; with no `entry` it opens the one the element is bound to. | {"command":"openEntry"} |
openPeek |
| card, board | — | No | Opens an entry in the peek overlay — a rail on desktop, a sheet on a phone — leaving the surface behind it visible. | {"command":"openPeek"} |
openView |
| board | — | No | Opens a saved view. | {"command":"openView","view":"8e0a3f00-0000-4000-8000-000000000000"} |
openDay |
| board | — | No | Opens one calendar day of a view in the day sheet. | {"command":"openDay","view":"8e0a3f00-0000-4000-8000-000000000000","date":"2026-01-01"} |
openInfo | — | card | — | No | Opens this collection's Description — the how-to behind the activity. | {"command":"openInfo"} |
createEntry |
| card, board, automation | — | Yes | Creates a REAL entry — through a preset when given one, dated `date` (default today), optionally ticking `property` on the new entry; `unlessExists: "same_day"` skips the create when one already exists that day. | {"command":"createEntry","preset":"8e0a3f00-0000-4000-8000-000000000000","unlessExists":"same_day"} |
startTimer |
| card | stopwatch | Yes | Starts the app-level timer aimed at this entry; stopping it writes the elapsed time into the stopwatch property. | {"command":"startTimer","property":"elapsed_6"} |
recordRoute |
| card | gps_distance | Yes | Starts the GPS recorder aimed at this entry; finishing writes the route and its run stats into the property. | {"command":"recordRoute","property":"route_8"} |
runAgent |
| automation | — | No | Hands an instruction to one of the user's AI agents and lets it run in its own chat. | {"command":"runAgent","agent":"navy_seal","instructions":"Review my week and tell me the one thing to fix."} |
effect |
| card, board | — | No | Plays one bounded motion on the element that was interacted with; every one collapses to nothing under `prefers-reduced-motion`. | {"command":"effect","name":"celebrate"} |
none | — | card, board | — | No | Does nothing, deliberately — it opts this element out of a handler inherited from its ancestor, and is a real answer rather than a fall-through. | {"command":"none"} |
Next
- Events — the
onblock that holds these - Expressions — where the conditionality went
- Cards — the elements you hang handlers off
- Automations — commands that run without a tap