@lotics/cli 0.316.11 → 0.316.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.js +55 -11
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -1729,7 +1729,7 @@ async function saveSignedLinks(links, dir) {
|
|
|
1729
1729
|
}
|
|
1730
1730
|
|
|
1731
1731
|
// src/version.ts
|
|
1732
|
-
var VERSION = "0.316.
|
|
1732
|
+
var VERSION = "0.316.12";
|
|
1733
1733
|
var APP_SDK_VERSION = "0.124.6";
|
|
1734
1734
|
|
|
1735
1735
|
// src/timezone.ts
|
|
@@ -1761,7 +1761,7 @@ var app_architecture_default = '# App architecture \u2014 a worked split\n\nThe
|
|
|
1761
1761
|
var working_in_a_workspace_default = "# Working in a workspace \u2014 the right means for each job\n\nOnce tables and apps exist, an agent does the team's work through them. The means depends on what the work is.\n\n| The work | The means | Why |\n|---|---|---|\n| A business operation: move an order on, issue a paper, notify someone | `run_app_workflow` on the app's act, `act_<key>` or the `workflow` it names | The outcome equals a person's press: status, stamps, documents, notifications. `update_records` on an act's field skips all of them. |\n| Data entry, a correction, loading the owner's lists | `create_records`, `update_records` | The table's rules hold for every writer. |\n| A rule or policy the business wrote | `get_workspace` with `search`, then `read_knowledge` | The business's own words, not a guess. |\n| A file someone hands over | `read_file_content`; `upload_file` to keep it (over MCP or the CLI) | Reads its content; the upload files it in the workspace. When your host passes no bytes, `upload_file` with `person: true` gives the person a link to drop it on. |\n| A paper an app makes | the act that makes it, which files it on the record | `generate_document` makes an unattached file, for a paper no app makes. |\n| A connected service (KiotViet, MISA, a carrier) | a workflow step, reached through a Workflow node | No tool calls a service directly (`workflows/reaching-a-connected-service`). |\n| A figure or list the business defines, or an operation it repeats | the app's declared Query or Workflow node: `run_app_query`, `run_app_workflow` | The node is the business's own definition, found by `get_workspace` with `search` and read in `get_app`. |\n| A count or sum no app answers | `aggregate_records` (grouped by `group_by`), `query_records` | |\n\nFind the means in this order: `get_workspace` maps the workspace; `search` it by what is asked (\"overdue\"), which matches an app's declared Query and Workflow keys and descriptions and names the matching keys under the app; `get_app` lists that app's queries and workflows with their inputs. Run the declared node. Only a question no node answers goes to `aggregate_records`.\n\n## Workspace settings and standing instructions\n\n`get_workspace` shows the workspace's settings (name, timezone, default currency) and, with kind `instruction`, its standing instructions. `set_workspace` changes them in one all-or-nothing patch: a setting it leaves out is unchanged, and `instructions` is a map keyed by instruction name where an entry creates or merges and `null` removes. A workspace-wide instruction arrives with the connection, in the instructions a host reads when an agent connects; the answer of `set_workspace` names any that no longer fit that block. An administrator writes them.\n\n## People\n\n`get_workspace` with kind `member` lists the members. `set_member` is an admin's: `members` is a map keyed by an email address or a member id, where an email no member holds invites that person (`role` is `member` or `admin`; the invitation email goes out), a current member's entry sets `name`, a removed member's entry restores them, and `null` removes a member or cancels the invitation pending to that address. A member's role cannot be changed through it. The whole map is checked before anything is written or sent. A member's tool list does not carry `set_member`, `set_group` or `set_workspace`.\n\n## Doing an operation\n\n`get_app` lists the app's acts as `act_<key>` (or the `workflow` an Act names) with what each takes. `run_app_workflow` with `dry_run: true` writes nothing\nand answers what the run would do; the same call without it runs. An act refuses a row its conditions do not hold for,\nand says why (`agent_surface` \xA7 A refusal).\n\n## Writing records\n\n`create_records` takes 50 records a call, 20 or more of one shape in columnar form (`field_keys` once, then `rows`);\n`update_records` changes one or many. Both name fields and select options by alias, and `field_values` gives each\ntype's value. Every write meets the table's rules (`model/write-rules`): every broken rule is listed at once, and a\nlocked record answers under `blocked`. The owner's own lists (a menu with prices, customers, open orders) are rows of their\ntables, loaded this way after `set_tables`.\n\n## Answering a question\n\n1. `get_app`: a Reading's query (its `key`) is the business's own figure for it. `run_app_query` with `alias` and\n `params` returns it; `filters`, `fields`, `sort` and `count` narrow the result.\n2. A period's total is one call per period, each with `from` and `to` or a date filter. `aggregate` groups a result by\n a column (`by`), a number's bands (`bands`) or a date column's day (`day`), and sums one (`sum`).\n3. A question no app answers is `aggregate_records` (counts and sums grouped by a field) or `query_records`, over one\n table; `filters` is the grammar of both.\n\n## A worked day\n\nA customer asks for the status of order 1042, then the order is shipped:\n\n```\nget_workspace kind \"app\" \u2192 app_id of the sales desk\nget_app app_id \u2192 aliases: order_list, act_ship, \u2026\nrun_app_query app_id, alias \"order_list\", params {\"search\": \"1042\"}\nrun_app_workflow app_id, alias \"act_ship\", inputs {\"record_id\": \"rec_\u2026\"}, dry_run true\nrun_app_workflow the same call without dry_run\naggregate_records table_id, group_by \"stage\" \u2192 how many orders stand at each stage now\n```\n";
|
|
1762
1762
|
|
|
1763
1763
|
// src/model_reference.md
|
|
1764
|
-
var model_reference_default = '# Tables and apps\n\nThe tables of a workspace and the apps over them, each a JSON document. A table is a document, an item of\nthe tool `set_tables`: its fields, options, views, how a row is recognised, and the rules its writes\nfollow. An app is a tree of components, the `body` of the tool `set_app`: one working register over one table, or a\ndashboard of readings. Each \xA7 is its own page, `model/<section>`.\nWhich treatment each job\'s screen needs: the `design` reference. Each treatment worked through in an\napp: the `examples` reference.\n\n**What the documents compose with**\n\n- **Tables and fields** (\xA7 Tables, \xA7 Read scope, \xA7 Field) \u2014 a table\'s columns, options and links. A table document sits\n in the `tables` list of a `set_tables` call; a field sits under its alias in the table\'s `fields`.\n- **Records** (\xA7 Records, then \xA7 Status) \u2014 how a row of each table is RECOGNISED: its title, the line\n under it, its picture, its status, the one number it stands for. Stated once per table\n and read by every surface that draws one of its rows.\n- **Write rules** (\xA7 Write rules) \u2014 what a write finds, bounds and picks among.\n- **Apps** (\xA7 App tree, then a page each: \xA7 Changing an app, \xA7 What an app may write, \xA7 App node, \xA7 List node, \xA7 Layouts, \xA7 Time and place layouts, \xA7 Publish desk,\n \xA7 Record node, \xA7 Section node, \xA7 Side node, \xA7 Page node, \xA7 Outside node, \xA7 Tab node, \xA7 Field node, \xA7 Act node, \xA7 Check node,\n \xA7 Reading node, \xA7 Progress node, \xA7 Statement node, \xA7 Export node, \xA7 Book node, \xA7 Query node, \xA7 Workflow node,\n \xA7 Composed page, \xA7 Tasks, \xA7 Rules of the app, \xA7 Register rules, \xA7 Block rules, \xA7 Record and act rules) \u2014 one working register over one table and the record each row opens,\n with the logs beside it: which fields go where, the acts, and the checks that guard them \u2014 or a dashboard of readings.\n How each thing LOOKS is the runtime\'s, one treatment per concept; no prop here changes it.\n A screen the job needs and no component states is told to the person \u2014 the job, what the app\n drew, the word wanted \u2014 never props bent to approximate it.\n\n**The working order**\n\n1. The tables and fields the jobs touch, and a `records` entry for every table an\n app lists, opens or picks. Which apps a business needs is `first_build`, \xA7 3.\n2. One `set_tables` call with the tables: a link names the table it points at by the `ref` of another\n item of the call, and the answer carries every table id and the aliases of the fields it added. A\n call carries at most 50 tables, 200 first rows a table, and 2000 rows and 2000 documents in all; more\n tables go in a further call, which names the earlier tables by alias. A call is checked whole and writes\n nothing when refused, listing everything wrong; `dry_run` is for reading the grants a write would make or\n deciding `force`.\n3. One `set_app` call per app. The tree names its tables and document templates by alias wherever it points at one;\n the App node\'s `tables` and `templates` maps are filled by `set_app`, never written.\n `set_app` with the tree as `body` creates the app, named by its `name`; a refused tree writes nothing and is listed whole, and `dry_run` is for reading the permissions the write\n would grant and remove (on the CLI, `lotics app apply <file>`, with `--check` for `dry_run`).\n4. Change an app afterwards one node at a time: `get_app` prints the `tree:`, each node\'s address under `node addresses:`\n and the `version` line, which is the `base_version_id` to send (the tree and addresses go to the app\'s author alone);\n `set_app` with `app_id`, `base_version_id` and `nodes`, a map from a node\'s address to a\n patch, changes it (`model/changing-an-app`), and its answer states the permissions the change grants and removes. A table\n is changed by `set_tables` with its `table_id`, a merge patch over the document `get_table` answers. To go back,\n send the tree you saved before the change as `body` with `app_id`: it replaces `children` but keeps every App prop it\n omits (`words`, `scope`, `period`, `icon`), so null those. (On the CLI, `lotics app pull <app_id>` writes the tree as JSON and `lotics app apply\n <file>` sends an edited file, refused when the app has moved past the version the file was pulled at.) An app\n saved before apps were trees has no tree: `get_app` says so beside what it lets a person write, and `set_app` with\n `app_id` changes it by its whole tree as `body`, which replaces it, or by its name, description, icon and color alone.\n\n## The rules\n\n- **A `set_tables` call is all or nothing.** Every item is checked before any is written; an item\n without `table_id` makes a table, one with it patches that table.\n- **An app\'s labels decide its language.** A body names no locale \u2014 the words an\n app draws itself (its buttons, empty states, refusals) are in the language its\n own labels are written in, so a Vietnamese desk beside English ones answers in\n Vietnamese. The generated screens read the kit\'s\n pack, and a generated WRITE cannot: its refusals run on the server, so they are\n worded in that same language. Mix the two within one app and it answers in two. The app\'s own `words`\n (its words for a table, a field or an option) count as its labels: Vietnamese words over an English table\n make the app answer in Vietnamese.\n- **`set_tables` and `set_app` check all of it before anything is written**, and\n report every problem in one run rather than the first. The tables half of this\n page ends its keys with the rules the check enforces there, and \xA7 Rules of the app holds the app\'s, one sentence each\n under an id; a finding names its rule\'s id (`[register.filter]`), and\n `model/<rule id>` is that one rule\'s page.\n\n<!-- generated:start rules-model -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `model.schema` | Every key is one its table lists, holding the type its row gives, and every required key is stated. |\n| `model.names-declared` | Every alias a key names is declared: a table of the App\'s `tables`, a field of the table the node reads, an option of the select it names, an Act `key` of the App, a template of its `templates`, or an app. |\n| `model.tables` | A call declares at least one table and at most 50; send the rest in a second call. |\n| `model.rows-cap` | Rows are a sample: at most 200 per entity, 2000 per call and 2000 documents attached \u2014 a real data set goes in with `create_records` once the tables exist. |\n| `model.alias-unique` | An alias is unique where it is named: tables, fields and views in their table, options in their select. |\n| `model.label-unique` | A label is unique where it is found by its label: fields and views in their table, options in their select \u2014 and no view takes its table\'s own label, which names the whole-table grid. |\n| `model.template-kind` | An Act\'s `template` is an html, excel or word document template and an Export\'s `template` an html or excel one, never an email or pdf-form one. |\n| `model.template-marker` | Every marker of a Word template an Act makes is filled by that Act: a field of its entity, or a list of the child rows its record lists (the entity\'s own alias over several rows). |\n| `model.template-act` | *Noted, never refused:* In an App none of whose Acts runs an authored `workflow`, every html, excel or word template is made by an Act (`template`, `templates`) or filled by an Export \u2014 the note names the app over the table whose fields it prints most, where the Act belongs. |\n| `model.filter` | A filter \u2014 a view\'s, a rollup\'s \u2014 tests fields of the entity it reads, through links as `entity.field` hops each standing on the entity the last lands on, by options its select declares. |\n\n<!-- generated:end rules-model -->\n\n## Top level\n\nEvery table of keys on this page is generated from the grammar a table document and an app tree are written in, so it is the whole of what a key may hold.\n\n<!-- generated:start top-level -->\n\n#### set_tables call\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `tables` | list of [Table item](#table-item) | yes | One or more, all or nothing; each item is a table document with these keys added. It creates a table (a `label` and `fields` at least) or, with `table_id`, patches one: the document it states merges into the table\'s own |\n| `rows` | map of alias \u2192 list of [Row](#row) | no | First records of the tables the call creates, keyed by each one\'s `ref` |\n| `force` | boolean | no | Consent to losing what rows hold: a field deleted, an option removed |\n| `dry_run` | boolean | no | Answer what the call would change, writing nothing: for reading the grants a write would make or deciding `force`. A refused call writes nothing too and lists every reason, so a dry run checks no more than the call does |\n\n#### Table item\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `ref` | alias | no | A created table\'s handle in this call: other items link to it, and `rows` are keyed by it |\n| `table_id` | text | no | The table to patch; absent, the item creates one. A patch states only what changes: `null` removes a key, a list replaces whole |\n\n<!-- generated:end top-level -->\n\n**A `set_tables` item is a table document** (\xA7 Tables) with `ref` and `table_id` added, and the call\ntakes no other key. **A `set_app` body is an app tree** (\xA7 App tree), whose root is an App node: its `tables` and\n`templates` each map a local alias the tree names to the id of the table or document template it means, from the\n`set_tables` answer or `get_table`. The tree is the only home of the app\'s `name`, `description`, `icon` and `color`;\n`set_app` takes it whole to create, or as a merge patch over the App node with `app_id`, or `nodes` with `app_id` to change one, and with `base_version_id` (the\nversion you read) it refuses a write over a newer one. Nothing is built code. An unknown key is an error, never ignored.\n\nA smallest call, two tables of plain fields, a select and a two-way link \u2014 nothing else is needed to write it:\n\n```json\n{\n "tables": [\n {\n "ref": "customer",\n "label": "Customers",\n "singular": "Customer",\n "fields": {\n "name": {"type": "text", "label": "Name", "required": true},\n "tier": {"type": "select", "label": "Tier", "options": [{"alias": "gold", "label": "Gold", "color": "yellow"}, {"alias": "standard", "label": "Standard", "color": "gray"}]},\n "orders": {"type": "link", "label": "Orders", "table": "order", "multi": true, "pair": "customer"}\n }\n },\n {\n "ref": "order",\n "label": "Orders",\n "singular": "Order",\n "fields": {\n "code": {"type": "text", "label": "Code", "required": true},\n "customer": {"type": "link", "label": "Customer", "table": "customer", "multi": false, "pair": "orders"},\n "amount": {"type": "number", "label": "Amount"}\n }\n }\n ]\n}\n```\n\n### Aliases\n\nEvery `alias` is a lowercase slug \u2014 a letter, then letters, digits and\nunderscores (`unit_price`, `so_1001`). Aliases are how a document and an app tree\nname a field, an option, a view or a table; they are never shown to anyone. `label` is\nwhat a person sees.\n\nLabels must be unique within their namespace \u2014 two fields on one table, two options\non one field, two views on one table cannot share a label.\n\n## Tables\n\n<!-- generated:start table -->\n\n#### Table document\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `label` | text | yes | The table\'s name in the workspace |\n| `singular` | text | no | One row of this table, in the business\'s own words \u2014 what a create\'s button and panel name |\n| `description` | text | no | The table\'s description, written onto the table in the workspace, where a caller of an app\'s queries and workflows reads it beside them |\n| `fields` | map of alias \u2192 [Field](#field) | yes | The table\'s columns, each under its alias, which is its key here and so is never stated inside it |\n| `read_scope` | [Read scope rule](#read-scope-rule) | no | Which rows a member reads. Absent, every member with access to the table reads every row. |\n| `unique` | list (at least one) of list of alias (at least one) | no | Sets of fields whose values no two live rows share \u2014 each a list of field aliases (text, number, date, a single select, or a link holding one row \u2014 `multi: false`); a single text field states `unique: true` on itself instead of a one-field set. A create or update landing a second row with the same values is refused. |\n| `views` | list of [View](#view) | no | Saved views, in the order they are listed; with none, the table still opens on its default grid |\n| `title` | alias | no | The alias of the field that names a row wherever a link or a record shows one; absent, the first text field |\n| `colors` | any value | no | Row colors: a list of { filters, color }, the filter reading fields of this table by alias and a select\'s options by alias |\n| `records` | any value | no | How a row is recognised: the keys of the Record, Status and Cadence tables below but `title`, which sits above; a table they name is an id or a `ref`. A status `history` that names a `ref` no item of the call declares is made by the call as a table, `dates` add each date field they name, and a status with stages each stage and each open option they name none for its `<option>_at`, unless the document declares it; each is listed in the result\'s notes |\n| `natural_key` | list of alias (at least one) | no | The field aliases a row of this table is RECOGNISED by: an app\'s add and find-or-create reuse the row they find by these values, minting one only where nothing matches. A record tool or the API creates the row it is handed. |\n| `no_overlap` | [No overlap](#no-overlap) | no | No two rows hold overlapping spans \u2014 a create or update that would is refused, and so is a change to the dates or states of the row a span is read through that would make two rows overlap |\n| `checks` | map of alias \u2192 table check (`workflows/table-checks`) | no | The conditions a write of the table\'s rows may not meet, each under its alias: a merge patch edits one, and `null` removes it. Expressions name fields by alias |\n| `workflows` | map of alias \u2192 table workflow (`workflows/table-workflows`) | no | The effects that follow every committed write of the table\'s rows \u2014 the grid\'s, an import\'s, an agent\'s, any app\'s \u2014 each under its alias: a merge patch edits one, and `null` removes it. A body names fields, options and tables by alias |\n\n<!-- generated:end table -->\n\n<!-- generated:start rules-entity -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `entity.singular` | A table a List\'s `create` is mounted over \u2014 the register or a child List that adds rows \u2014 states its `singular`: one row of it, in the business\'s words. |\n| `entity.required-cycle` | Required links never wait on each other in a loop: no row of the loop could be created first. |\n| `entity.link-format` | A text field of `format: "link"` holds web addresses; one whose rows hold a phone or a mail address states no format. |\n| `entity.read-scope` | A `read_scope` clause names a group (`member_of`) or a field of its table reached through one-row links: `"self"` a member field, options a single select declares; an `any` or an `all` groups clauses, and groups nest. |\n| `entity.unique` | A `unique` set names two fields or more of its table once each, each holding one value to compare \u2014 text, a number, a date, a single select, a one-row link \u2014 and is stated once; one field alone is `unique: true` on it. |\n\n<!-- generated:end rules-entity -->\n\n**`unique` is a set of values no two live rows share.** Each entry names fields\nof this table holding ONE value \u2014 text, number, date, a single select, a link\nwith `multi: false` \u2014 and a create or update landing a second row with the\nsame values is refused. A set of one text field is that field\'s own `unique:\ntrue`, so it is refused here. A create carries each set in the names its panel\nsends, so the panel can name the duplicate before the write does.\n\n**`singular` is what a create says** \u2014 `New Order`, `Add Claim line`, `H\u1ED3 s\u01A1\nm\u1EDBi` \u2014 while `label` names the table, so without it the button reads `New\nOrders`. Nothing derives it: English plurals are irregular, and no language is\nexempt. In a language without plural forms it is usually the label itself,\nless any word for the collection. A List that leaves `create` to its default adds nothing over a table that states\nnone (nor does the app\'s `words` state one); a `create` list stated over such a table is refused (`entity.singular`).\n\n**An app that only reads a table\'s rows draws them with `edit: false`** and states `create: false` and `remove: false` \u2014 a log of what the system sent, a copy of\nwhat another system holds. The app keeps the section and opens each row at rest, with no Add and no Delete; a party\nof it is picked, never found or minted; and the app writes no create, update or remove for it. It shapes that app, not who may write: a direct write \u2014 the records\nAPI, the chat agent, an import \u2014 follows the table\'s access, as on any table, and another app over the same rows\nstates its own tree.\n\n**`read_scope`** \u2014 which rows a member reads \u2014 is its own page, `model/read-scope`.\n\n## Read scope\n\n<!-- generated:start read-scope -->\n\n#### Read scope rule\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `any` | list of ([Read scope by role](#read-scope-by-role) \\| [Read scope by member](#read-scope-by-member) \\| [Read scope by option](#read-scope-by-option) \\| [Read scope rule](#read-scope-rule)) (at least one) | no | A row is readable when ANY of these holds |\n| `all` | list of ([Read scope by role](#read-scope-by-role) \\| [Read scope by member](#read-scope-by-member) \\| [Read scope by option](#read-scope-by-option) \\| [Read scope rule](#read-scope-rule)) (at least one) | no | A row is readable when ALL of these hold |\n\n#### Read scope by role\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `member_of` | text | yes | The id of a group (from Settings, or chat): whoever is in it reads the row |\n\n#### Read scope by member\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `through` | list of alias (1\u20133) | no | link aliases from this entity outward, each holding one row (`multi: false`) \u2014 the clause\'s field is on the entity the last hop lands on |\n| `field` | alias | yes | A member field alias on this entity, or on the entity `through` lands on |\n| `is` | `"self"` | yes | The members this column names on a row read that row |\n\n#### Read scope by option\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `through` | list of alias (1\u20133) | no | link aliases from this entity outward, each holding one row (`multi: false`) \u2014 the clause\'s field is on the entity the last hop lands on |\n| `field` | alias | yes | A single-select field alias on this entity, or on the entity `through` lands on |\n| `is` | list of alias (at least one) | yes | Its option aliases whose rows are readable \u2014 naming none would hide every row |\n\n<!-- generated:end read-scope -->\n\n**`read_scope` is a ROW rule, enforced by the platform.** It is resolved into the table\'s own row\nfilters, so an app, a workflow reading for a viewer, and the API all answer the same rows \u2014 a per-record\nvisibility field the app merely honours is a convention, not a gate. A `"self"` clause reads a column\nof one member or several, and every group and option a clause names must exist.\n\n**An app reads every table under the table\'s own `read_scope`**, and a tree has no key to lift it. A desk whose audience\nits sharing already decides is let into the rule by a group that holds that audience (`member_of`), stated on the\ntable.\n\n**A row rule is never inherited.** A child read under a private record states\nits own: the record\'s `member_of` clauses as they are, and its column clauses\n`through` the one-row link the child hangs by \u2014 over one hop or several, each a\nlink with `"multi": false`, since over several rows the rule would admit a\nreader any one of them admits while the editor\'s guard reads the first. Only the\nstatus history takes its record\'s rule.\n\n## Field\n\nEvery field carries the keys below, and its `type`\'s section adds the rest; a\ntype whose section names no `default` takes none. `label` may not contain `{` or\n`}` (formulas reference fields by label at the platform level). Every row in `rows` states each\n`required` field it carries (neither a default nor a `default_from` copy is applied to them), and a required\nLINK is written with its row: the table it names is created first, and tables\nwhose required links name each other are refused, since none of their rows could\never be created.\n\n**A table states who keys nothing.** Whether a person may change a field in an app is that app\'s statement\n(what its tree draws, and `edit: false`, \xA7 App tree): a post\'s id a publishing workflow brings back, a count a sync fills, a status an act moves\nare left out of the apps that do not save them, and written by a workflow, the records API, the chat agent and an\nimport as the table\'s access allows. A formula, a rollup, a lookup and an autonumber are no one\'s to key already.\n\n<!-- generated:start field -->\n\n#### Field\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `type` | `"text"` \\| `"number"` \\| `"date"` \\| `"boolean"` \\| `"select"` \\| `"member"` \\| `"link"` \\| `"files"` \\| `"formula"` \\| `"rollup"` \\| `"lookup"` \\| `"autonumber"` | yes | What the field holds \u2014 each type takes the further keys its own section lists |\n| `label` | text | yes | The field\'s name in the workspace |\n| `description` | text | no | The field\'s description, written onto the field in the workspace |\n| `required` | boolean | no | Refuse a record whose cell for this field is empty. Every write path \u2014 create, update, an agent\'s tool call, a workflow\'s set \u2014 refuses the row by field name. A stored field is flagged only once every row holds a value. |\n\n<!-- generated:end field -->\n\n### `text`\n\n<!-- generated:start field-text -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | text | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. |\n| `unique` | boolean | no | Unique values required |\n| `format` | `"text"` \\| `"link"` \\| `"markdown"` | no | How the words are drawn \u2014 plain, as a link that opens, or as markdown |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `suggest` | [Suggest](#suggest) | no | A text field whose control offers, as the reader types, the distinct values a text field of a catalog table holds (a damage position\'s code among the codes on file); any text is still written |\n\n<!-- generated:end field-text -->\n\n### `number`\n\n<!-- generated:start field-number -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | number | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. |\n| `notation` | [notation](#number) | no | How the figure reads: { "style": "decimal" }; money, { "style": "currency", "currency": an ISO 4217 code (USD, VND) }; or a quantity, { "style": "unit", "unit": "percent" (10 reads "10%"), or a measured code (g, kg, t, l, m3, cbm, mm, cm, m, km, m2, min, h, day), which converts and scales within its dimension, or any other noun of at most 12 characters (ki\u1EC7n, pallet, TEU), which never does }. A currency or unit read on each row from a single select of that row is { "per_row": that select }. Omitted, a decimal. |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `min` | number \\| text | no | Refuse a create or update whose value is below this: a constant for a number, or a field \u2014 `<field alias>` of the same row, or `<link alias>.<field alias>` of the one row a link names \u2014 of the same type (a return date after the departure, a line\'s day inside its trip). On a figure whose unit or currency is per row (its notation\'s `per_row`) a constant bound is only 0; bound it by a field of the row |\n| `max` | number \\| text | no | Refuse a create or update whose value is above this: a constant for a number, or a field of the same row or of the one row a link names |\n\n<!-- generated:end field-number -->\n\n`notation` is how the number reads, and every surface reads it:\n`{ "style": "currency", "currency": "USD" }` prints as money in its code,\n`{ "style": "unit", "unit": "kg" }` as a quantity with its unit after it,\n`{ "style": "unit", "unit": "percent" }` as a percent \u2014 10 is "10%", never\nmultiplied \u2014 and `{ "style": "decimal" }`, or no notation, as a plain number. One\nkey, so a figure reads one way.\n\nAn absent number is drawn blank, except a `sum` or count rollup read as a meter\'s measure: nothing accumulated yet reads 0.\n\nA unit \u2014 `{ "style": "unit", "unit": "kg" }` \u2014 is what a figure counts or\nmeasures; `percent` aside, it is measured or counted. A **measured** unit is a code of one catalog \u2014 mass `g`\n`kg` `t`, volume `l` `m3` `cbm` (a cubic metre under freight\'s name), length\n`mm` `cm` `m` `km`, area `m2`, duration `min` `h` `day` \u2014 so a figure typed in\nanother unit of its dimension converts (`12,5 t` into a `kg` field is 12 500),\nand a tile or a chart reads it in the largest unit it reaches (12 500 kg as\n12,5 t\u1EA5n; CBM never scales) while a cell, a fact and a column always read in the\nfield\'s own. Any other noun of at most 12 characters (`ki\u1EC7n`, `pallet`, `TEU`) is a\n**counted** unit: a word after the figure that never converts. A measured unit is\nwritten as its code \u2014 `t\u1EA5n`, `KG` or `m\xB3` is refused, naming the code. A formula\nstates its own as its `notation`; a rollup that keeps the value (`sum`, `avg`,\n`median`, `min`, `max`, `range`) and a lookup carry the unit of the figure they\nread, exactly as they carry a currency, and a count carries none. A quantity is a\nnumber with its unit, never words (`3 cartons` in a text field): only a number\nsums, converts and reads down a column.\n\nA field\'s cells always hold figures in its own unit, so moving it between two\nunits of one dimension (`kg` to `t`) converts every stored figure \u2014 a change made\nin the workspace, in the field\'s settings (which say how many first) or through\n`set_tables`. Any other change of unit\nrelabels.\n\nA figure whose unit or currency varies by row names a single select of its own\nrow as `per_row` \u2014 `{ "style": "unit", "unit": { "per_row": "uom" } }`,\n`{ "style": "currency", "currency": { "per_row": "currency" } }` \u2014 or a lookup of\none through a one-link, a line reading its shipment\'s currency. That select\'s\noptions are the vocabulary: each label a unit (`chi\u1EBFc`, `kg`), or an ISO 4217 code\n(`USD`). A label outside it is refused when the figure is written and when the\nselect is \u2014 its options, its type or its deletion while a figure names it. A row\nwhose select is empty reads its figure bare. Every app reads each row\'s figure in\nits own row\'s unit, and a sum never mixes units: totals are one per unit (past three units a total the runtime draws over a set of rows \u2014\nsummary line, group totals and feet, a dashboard\'s table foot \u2014\nstates the count alone; a reading an author states draws every unit), a chart draws one at a time. A rollup that keeps the value (`sum`, `avg`, `min`\u2026)\nover such a figure stands only where the child\'s select is a lookup, through\nthe rollup\'s own link, of a select on this table \u2014 the rollup reads that\nselect\'s unit; otherwise it is refused. A lookup of such a figure stands only\nwhere its table also looks up the figure\'s select through the same one-link,\nand reads in that lookup; otherwise it is refused. A count is unaffected. Moving a field between a\nfixed and a per-row unit relabels.\n\n### `date`\n\n<!-- generated:start field-date -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | text | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. A date string in the field\'s format. |\n| `format` | `"date"` \\| `"datetime"` \\| `"date_range"` \\| `"datetime_range"` | no | Whether the field holds a day or a moment, alone or as a span |\n| `timezone` | text | no | IANA timezone |\n| `derive_from` | `"created_at"` \\| `"updated_at"` | no | Auto-populate from the row\'s system timestamp; the field becomes read-only. |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `min` | number \\| text | no | Refuse a create or update whose value is below this: a constant for a number, or a field \u2014 `<field alias>` of the same row, or `<link alias>.<field alias>` of the one row a link names \u2014 of the same type (a return date after the departure, a line\'s day inside its trip). On a figure whose unit or currency is per row (its notation\'s `per_row`) a constant bound is only 0; bound it by a field of the row |\n| `max` | number \\| text | no | Refuse a create or update whose value is above this: a constant for a number, or a field of the same row or of the one row a link names |\n\n<!-- generated:end field-date -->\n\nA `default` is refused beside `derive_from`: the platform stamps that date.\n\n### `boolean`\n\n<!-- generated:start field-boolean -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | boolean | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n\n<!-- generated:end field-boolean -->\n\n### `select`\n\n<!-- generated:start field-select -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | list of alias | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. Option alias(es) this field declares \u2014 one for single-select. |\n| `options` | list of [Select option](#select-option) (at least one) | yes | The choices, in the order every picker and every ladder lists them |\n| `multi` | boolean | no | Allow multiple selections |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n\n#### Select option\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `alias` | alias | yes | Stable local alias, unique within the field |\n| `label` | text | yes | Display label for the option |\n| `color` | [colour](#select) | no | The colour the option\'s badge is drawn in; absent, the platform gives it one no sibling option holds |\n| `mark` | [mark](#select) | no | The option\'s own mark, drawn in place of its colour dot wherever the option is shown: the brand it is ({kind: "brand", name: one of facebook, instagram, threads, meta, tiktok, google-ads, zalo, linkedin, x, google-meet, youtube, telegram, whatsapp, gmail, google-drive, outlook, kiotviet, misa, lark, payos}) a kit glyph ({kind: "icon", name: "wrench"}) or the face of who it is, drawn from its label ({kind: "avatar", name: "person" or "organization"}). Every option of a field has one, or none does. A mark a reader does not draw falls back to the dot |\n\n<!-- generated:end field-select -->\n\nA patch of a select states its whole `options` list: a list replaces whole, so an option it leaves out is deleted. To\nrename or recolour one option, restate every option kept, each with its alias. Rows still holding a deleted option refuse\nthe patch unless `force`.\n\n```json\n{\n "options": [\n { "alias": "draft", "label": "Draft" },\n { "alias": "confirmed", "label": "Confirmed" },\n { "alias": "shipped", "label": "Shipped", "color": "green" }\n ]\n}\n```\n\n`color` is one of: `red`, `orange`, `amber`, `yellow`, `lime`, `green`,\n`emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`,\n`fuchsia`, `pink`, `rose`, `slate`, `gray`, `zinc`, `neutral`, `stone`.\nEvery option is drawn in its colour wherever it shows \u2014 a cell, a row\'s line,\na record, a picker, the bar a reading splits the rows by.\n\nAn option may carry its own `mark`, drawn in place of its colour dot wherever\nthe option is shown \u2014 a stage, a chip, a filter, a fact, an entry of a log, a\nreading\'s part, a lookup of the select on another table. Where the select is its\nrows\' `records` `mark`, a brand or glyph stands on the row\'s lead tile in the option\'s\ncolour, and an option with none leads with its colour dot there. A select a reader scans\ndown a column \u2014 how a payment was made, the channel, the mode \u2014 states one on every\noption, since a glyph reads before its word: the brand it IS\n(`{"kind": "brand", "name": "tiktok"}` \u2014 one of `facebook`, `instagram`,\n`threads`, `meta`, `tiktok`, `google-ads`, `zalo`, `linkedin`, `x`,\n`google-meet`, `youtube`, `telegram`, `whatsapp`, `gmail`, `google-drive`,\n`outlook`, `kiotviet`, `misa`, `lark`, `payos`), a glyph the kit draws\n(`{"kind": "icon", "name": "wrench"}`; any other name is refused), or \u2014 where the\noptions name people or organisations who are no member and no row \u2014 the face of who\nit is, drawn from its label (`{"kind": "avatar", "name": "person"}` or\n`"organization"`). Every option of a select has one, or none does: a run of chips\nwhere one carries no mark reads as the one missing something. `set_tables` writes the\nmarks onto the table. The glyph names are `model/field/option-glyphs`.\n\n```jsonc\n"options": [\n { "alias": "short_video", "label": "Short video", "color": "zinc", "mark": { "kind": "brand", "name": "tiktok" } },\n { "alias": "print", "label": "Print", "color": "amber", "mark": { "kind": "icon", "name": "newspaper" } }\n]\n```\n\n### Option glyphs\n\nThe kit\'s glyph names an option\'s `mark` takes:\n\n<!-- generated:start field-select-glyphs -->\n\nactivity, align-center, align-left, align-right, align-vertical-justify-center, align-vertical-justify-end, align-vertical-justify-start, arrow-down, arrow-down-a-z, arrow-down-up, arrow-down-wide-narrow, arrow-left, arrow-left-from-line, arrow-right, arrow-right-from-line, arrow-right-left, arrow-up, arrow-up-down, arrow-up-wide-narrow, arrow-up-z-a, ban, banknote, bed, bell, between-horizontal-start, between-vertical-start, bold, bolt, book-marked, book-open, book-text, bot, box, brackets, brain, briefcase, building-2, calculator, calendar, calendar-clock, calendar-off, camera, car, chart-column, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down-up, chevrons-up-down, circle-alert, circle-check, circle-x, clipboard-list, clipboard-paste, clock, code, code-xml, columns-3, columns-3-cog, combine, construction, container, copy, credit-card, database, decimals-arrow-left, decimals-arrow-right, dollar-sign, download, ellipsis, eraser, expand, external-link, eye, eye-off, facebook, file, file-csv, file-down, file-question, file-spreadsheet, file-stack, file-text, file-up, folder, folder-closed, folder-open, folder-pen, form, funnel-plus, funnel-x, gauge, globe, gpu, grid-2x2, grip-vertical, group, hand-coins, hash, heading, heading-1, heading-2, heading-3, history, house, image, inbox, info, instagram, italic, keyboard, languages, layout-dashboard, layout-grid, library-big, link-2, link-2-off, linkedin, list, list-checks, list-collapse, list-filter, list-filter-plus, list-ordered, list-tree, loader, lock, lock-keyhole, lock-keyhole-open, lock-open, log-in, log-out, mail, map-pin, maximize-2, megaphone, menu, message-circle, message-circle-question-mark, message-square, messages-square, mic, minimize-2, minus, monitor, mouse, mouse-pointer-click, music, newspaper, notepad-text-dashed, package, paint-bucket, paintbrush-vertical, palette, panel-left, panel-left-close, panel-left-open, panel-right, panel-right-close, panel-right-open, paperclip, pause, pencil, percent, phone, pin, pin-off, plane, play, plug, plus, receipt, rectangle-ellipsis, redo, refresh-cw, repeat, rotate-ccw, rotate-cw, rows-3, scan, scan-line, scissors, search, send, settings, share, share-2, shield, shield-alert, shield-check, shopping-cart, sigma, sliders-horizontal, smile, smile-plus, snowflake, sparkles, split, square, square-check, square-pen, square-sigma, stethoscope, sticky-note, strikethrough, table, table-2, tag, target, text-quote, text-wrap, thumbs-down, thumbs-up, ticket, trash, trending-down, trending-up, triangle-alert, truck, tv-minimal, twitter, type, underline, undo, upload, user, user-check, user-pen, users, utensils, waypoints, workflow, wrench, x, zap, zoom-in, zoom-out\n\n<!-- generated:end field-select-glyphs -->\n\n### `member`\n\nA person picker over the workspace\'s members. No default. With no role it is still drawn \u2014\nface and name, ranked as a `party` \u2014 in a screen\'s `columns` and in the register\na record draws of these rows.\n\n<!-- generated:start field-member -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `multi` | boolean | no | Allow multiple selections |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n\n<!-- generated:end field-member -->\n\n### `link`\n\n<!-- generated:start field-link -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `options_where` | [filter](#views) | no | Which rows of the target this link may point at \u2014 an `and` group of plain conditions over the TARGET table\'s own fields, each `field_key` a field alias. It narrows every picker of the link, and a save naming a row outside it is refused on every path \u2014 an app, the grid, an import, a workflow, the API. |\n| `same` | list of alias (at least one) | no | One-row links this row and each row this link points at must name alike \u2014 a fee\'s invoice is one of its own visit\'s, a box\'s seal one of its own shipping line\'s: each a one-row link of both tables to the same table. It narrows the picker\'s read to the rows naming what this row names, a row made from the picker names it too (set, never asked), and a write is refused wherever it lands \u2014 on the link, or on what the two rows compare. |\n| `table` | text | yes | The table the link points at: its alias, or the `ref` of another item of the call |\n| `multi` | boolean | yes | true: the link holds many rows; false: one. Where the link is paired, at most one of the two sides holds one |\n| `pair` | `true` \\| alias | no | Makes the link two-way: the alias of its partner field on the other table, which names this one back, or `true` to have the alias derived. Stated on one table, the partner is made on the other with the inverse cardinality; a many-to-many link states both sides |\n\n<!-- generated:end field-link -->\n\n**`multi` is stated on every link:** `true` holds several rows, `false` one \u2014 one customer on an\ninvoice, one project on a device. There is no default.\n\nA two-way link states `pair` on one table, as `true` or a free alias, and the partner is made on the\nother table with the inverse cardinality; a many-to-many link states both sides, since the inverse of\nmany is not decided. A pair stated on both sides is made once. Both tables are written in the one\ncall (`table` names the other item\'s `ref`). A link with no `pair` is one-way, and nothing is created on the target. Pair a link when\nthe target\'s own record should list what points at it, and leave it unpaired when it should not: either\nway the record plan draws the relation as a section on the side it points at, so an unpaired link costs\nthe target nothing. Of two paired links at most one holds one row.\n\n```jsonc\n// a field of table "order"\n"customer": { "type": "link", "label": "Customer", "table": "customer", "multi": false, "pair": "orders" }\n```\n\n```jsonc\n// a field of table "customer"\n"orders": { "type": "link", "label": "Orders", "table": "order", "multi": true, "pair": "customer" }\n```\n\n### `files`\n\nA row attaches documents to it (\xA7 Rows). Each file\nis drawn by its kind, with no key to choose: a picture (an image, a video) as its\nthumbnail, a document (a PDF, a sheet) as its type\'s badge and its filename.\n\n### `formula`\n\nA formula states its keys on the field itself, beside `type` and `label`:\n`{ "type": "formula", "label": "Total", "expression": "{quantity} * {unit_price}", "notation": { "style": "currency", "currency": "USD" } }`.\n\n<!-- generated:start field-formula -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `expression` | text | yes | The expression, over fields of THIS entity by alias in braces \u2014 `{quantity} * {unit_price}` |\n| `output_type` | `"number"` \\| `"text"` \\| `"date"` \\| `"datetime"` \\| `"boolean"` \\| `"select"` | no | Absent, it is what the expression yields. Stated, it is checked against that and refused where they differ (a datetime is never stated date) |\n| `notation` | [notation](#number) | no | How the figure reads: { "style": "decimal" }; money, { "style": "currency", "currency": an ISO 4217 code (USD, VND) }; or a quantity, { "style": "unit", "unit": "percent" (10 reads "10%"), or a measured code (g, kg, t, l, m3, cbm, mm, cm, m, km, m2, min, h, day), which converts and scales within its dimension, or any other noun of at most 12 characters (ki\u1EC7n, pallet, TEU), which never does }. A currency or unit read on each row from a single select of that row is { "per_row": that select }. For a formula yielding a number. |\n| `link` | `true` | no | The formula yields a URL as text, drawn as a link that opens. |\n| `options` | list of [Select option](#select-option) (at least one) | no | The categories the formula yields, drawn as a single select\'s options are (read-only). The expression yields one of them as `{this_field:option}`, or null \u2014 `{days_idle} > 30 ? {warmth:cold} : {warmth:hot}`. Omit for a formula yielding a plain value. |\n\n<!-- generated:end field-formula -->\n\n`output_type` is what lets a role or a screen clause accept a computed value: a\ncaption over a derived name (`output_type: "text"`), a period over a settled date\n(`"date"`). A formula declaring no `output_type`, `notation` or `link` says nothing\nabout its result, and every rule that needs one refuses it by name. A formula stating\n`options` is a computed category: read-only, and read as a single select\nwherever it is drawn \u2014 a column, a filter, a reading\'s `by` or `where`.\n\n**A select reaches a formula as the KEYS of its chosen options**, a list \u2014\nnever their labels, and never their aliases \u2014 and a document has no keys: the\nworkspace mints them when the table is made. So an option is named in a formula\nas `{field:option}`, both aliases, and the write puts that option\'s key in its\nplace: `includes({kind}, {kind:crate})` for a select holding one or several,\n`{kind}[0] == {kind:crate}` for a single one. A select compared to its own words\n(`{kind} != "Crate"`) matches no row and computes the other branch everywhere,\nso the check refuses it and names the token. A select looked up from another\ntable is tested there, in a formula of its own, and that result looked up.\n\n**The language.** The check refuses a call or a name the platform cannot run, naming what to write:\n\n<!-- generated:start field-formula-language -->\n\n- Operators: `+ - * / %`, `== != > < >= <=`, `&& || !`; `+` also joins text\n- Numbers: arithmetic is exact decimal (`0.1 * 3` is `0.3`), a division that does not end the nearest double; a comparison between two numbers reads both at 15 significant digits; `round` rounds a half away from zero (`round(-2.5)` is `-3`)\n- Conditionals: a ternary only \u2014 `{amount} > 100 ? "High" : "Low"`\n- Not supported: optional chaining (`?.`), nullish coalescing (`??`), template literals, arrow functions \u2014 use `get(obj, "path", default)`, `coalesce(v1, v2)`\n- Helpers are these names, spelled exactly; a spreadsheet function (`IF`, `SUM`, `LEN`, `DATEDIF`) is none of them, and a formula calling one is refused\n- Math: round(n,decimals?), ceil(n), floor(n), abs(n), min(a,b), max(a,b), sum(arr), mean(arr), clamp(n,min,max), percentage(part,total,decimals?), pow(base,exp), sqrt(n), mod(n,divisor)\n- Strings: upper(s), lower(s), trim(s), capitalize(s), length(s), contains(s,search), join(arr,sep), split(s,sep), replace(s,search,rep), replaceAll(s,search,rep), removeChars(s,chars) (s without any character that occurs in chars), removeSpaces(s) (s without any whitespace), startsWith(s,prefix), endsWith(s,suffix), substring(s,start,end?), padStart(s,len,char), padEnd(s,len,char), numberToWords(n, lang?) (lang \'vi\' default, or \'en\'), uuidFrom(s) (the same UUID for the same text)\n- Lists: includes(list,value), first(list), last(list), unique(list), compact(list) \u2014 length(list) counts one\n- Dates: now(), formatDate(d,fmt), addDays(d,n), subDays(d,n), addHours(d,n), subHours(d,n), addMinutes(d,n), subMinutes(d,n), startOfDay(d), endOfDay(d), differenceInCalendarDays(later,earlier), differenceInHours(later,earlier), differenceInMinutes(later,earlier), isBefore(d1,d2), isAfter(d1,d2), isSameDay(d1,d2), isToday(d), isWithinRange(d,start,end), meetsBounds(value,kind,{gt,gte,lt,lte},tz?), parseDate(d)\n- Null/type: isNull(v), isEmpty(v) (also true for "" and []), coalesce(v1,v2,...), isString(v), isNumber(v), isBoolean(v), isArray(v), toNumber(v), toFigure(v), toString(v)\n- Other: formatCurrency(amount,locale,currency), formatDecimal(value,decimals,locale) (grouped quantity, no symbol), get(obj,"path",default?)\n- Empty cells: a cell nobody filled is null inside a formula, whatever its type; one holding 0, false or "0" is not empty. Test it with `isEmpty({note})` \u2014 `{note} == ""` and `{done} == false` are false on an unset cell\n- Arithmetic over an empty cell: `+` and `-` read it as 0 beside a value (`{fee} + {surcharge}` is `{fee}` when the surcharge is empty, null when both are); `*`, `/`, `%` and a unary `-` yield null (`{price} * {qty}` is null, not 0, when the quantity is empty)\n- No helper throws on an empty cell: the math helpers and toNumber return null, the string helpers "". A value of the wrong type still errors. When EVERY field a formula reads is empty it is null \u2014 unless it reads each only as the argument of isEmpty, isNull or isNotNull (`!isEmpty({file})` is false there, not null)\n\n<!-- generated:end field-formula-language -->\n\n### `rollup`\n\nAggregates the records reached through a link on this table.\n\n<!-- generated:start field-rollup -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `filter` | [filter](#views) group | no | Only linked records matching this filter are aggregated; one condition on its own is a group of one. Every `field_key` in it names a field alias on the linked entity, and a select condition\'s value names an option alias there. A traversal node reaches past that entity, so its `path` hops and inner `field_key` are fully-qualified `entity.field` aliases. |\n| `via` | alias | yes | The alias of a link of this table to roll up from |\n| `op` | [operation](#rollup) | yes | The operation over the linked rows |\n| `field` | alias | no | The alias of the field on the linked table the operation reads; absent for `count` |\n\n<!-- generated:end field-rollup -->\n\nA rollup states `via` (a link of this table), `op` and \u2014 except for `count` \u2014 `field`, an alias on the\nlinked table; `op` is one of `count`,\n`sum`, `avg`, `median`, `min`, `max`, `range`, `empty`, `filled`,\n`percent_empty`, `percent_filled`, `unique`, `percent_unique`, `earliest`,\n`latest`, `date_range`, `checked`, `unchecked`, `percent_checked`,\n`percent_unchecked`. The operation must be one the aggregated field\'s type\nallows \u2014 `sum` over a number, `earliest` over a date, `filled` over any stored or\nformula field. A lookup is never rolled up: roll up the child\'s own field, or a formula\nover it. A rollup states no `notation`: `sum`, `avg`, `median`, `min`, `max` and `range`\nread in the aggregated figure\'s, a count as a plain number, and a `percent_*` as a\npercent (0\u2013100), in the app as in the table.\n\n### `lookup`\n\nDisplays a field from the linked records: `via` names a link of this table, `field` a field of the table it\npoints at, and `order_by` (`field`, `direction`) picks the one row whose value shows.\n\n<!-- generated:start field-lookup -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `via` | alias | yes | The alias of a link of this table to look up through |\n| `field` | alias | yes | The alias of the field on the linked table to show |\n| `order_by` | [Lookup order](#lookup-order) | no | Show ONE linked row\'s value \u2014 the first in this order \u2014 rather than every linked row\'s |\n\n#### Lookup order\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A field alias on the linked table the rows are ordered by |\n| `direction` | `"asc"` \\| `"desc"` | yes | Which end of that order the one row is taken from |\n\n<!-- generated:end field-lookup -->\n\nInside a formula, a lookup holding one value is that value (`{due_soon}` is\n`true`, not `[true]`); several values are a list.\n\n### `autonumber`\n\n<!-- generated:start field-autonumber -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `prefix` | text | no | Literal prefix prepended to every display value (e.g. \'KH-\' \u2192 \'KH-001\'). Ignored when `template` is set. |\n| `padding` | integer | no | Zero-pad the integer to this width. Default 1 (no padding). 3 \u2192 \'001\', \'012\', \'123\', \'1234\' (overflow uses the actual width). Ignored when `template` is set. |\n| `template` | text | no | Format template with placeholder tokens evaluated at insert time. Tokens: {N} (raw integer), {N:W} (zero-padded to width W, e.g. {N:3} \u2192 001), {YEAR} (4-digit year), {YEAR:2} (2-digit year), {MONTH} (2-digit month), {DAY} (2-digit day). Date tokens use the workspace timezone. Example: \'HM-{YEAR}-{N:3}\' yields \'HM-2026-001\'. Stored as the composed string; subsequent template edits do NOT re-format existing rows (date tokens would lose the original creation date). |\n| `next` | integer | no | Sets the number the next row takes, once, as the patch is written; never below one past the highest number issued. On a field added to a table with rows, those rows are numbered from it in creation order. It is no setting of the field: a read of the table shows none |\n\n<!-- generated:end field-autonumber -->\n\nA refused write and a dry run take no number, so a table\'s numbers run without a gap; a removed row\'s number is not\nissued again.\n\n<!-- generated:start rules-field -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `field.default` | A default names options its select declares \u2014 one on a single select \u2014 and a date stamped by `derive_from` states none. |\n| `field.default-from` | A field\'s `default_from` reads `<link>.<field>` off the row a one-row link of its table names \u2014 a link no copy fills \u2014 or, through further one-row links, `<link>.<link>.<field>` off the row the last names, into a field of the same type, date format and notation that `derive_from` does not stamp: a lookup only through a one-row link, a link only into one to the same table, a select\'s option as this field\'s of the same alias (one it lacks is not copied), several values only into a field holding several. The server copies it on every add that fills the link and leaves the field empty, so a List\'s `create` sure to fill the link \u2014 a required one, or the record it files the row under \u2014 does not ask it by default. |\n| `field.option-mark` | An option\'s `mark` is one the kit draws, and a select\'s options mark every one or none. |\n| `field.unit-select` | A notation\'s `per_row` names a single select of the same row, or a lookup of one through a one-link, whose every option label is a unit, or an ISO 4217 code. |\n| `field.link` | A link targets a table of the call or the workspace: its `pair` a link on that table naming this one back \u2014 and of two paired links at most one reads one row (`multi: false`). |\n| `field.formula` | A formula parses, reads fields of its own table by alias, names an option as `{field:option}` of one a select declares, and compares a select to its options\' keys, never their words. |\n| `field.formula-type` | A formula yields what it states \u2014 its `output_type` (the type the platform stores: a `datetime` is never stated `date`), else a figure where it states a `notation` and text where it states `link` \u2014 as the platform infers it from the expression when the field is created. |\n| `field.rollup` | A rollup aggregates, through a link of its table (`via`), a field of the linked table (`field`) by an operation (`op`) that field\'s type takes \u2014 never a figure read in each row\'s own unit or currency \u2014 filtered on fields of the linked table. |\n| `field.lookup` | A lookup reads (`field`), through a link of its table (`via`), a field of the linked table \u2014 a figure read in each row\'s own unit or currency only where this table looks up that row\'s select through the same one-row link \u2014 ordered by a field of it (`order_by`). |\n| `field.computed-cycle` | Computed fields never wait on each other in a loop: each is computed after what it reads. |\n\n<!-- generated:end rules-field -->\n\n## Views\n\nSaved views live under the table they belong to. Every field reference is a\nfield ALIAS on that table. A `set_tables` patch of an existing table refuses `views`: they are stated when the table is\ncreated, and change afterwards in chat.\n\n<!-- generated:start views -->\n\n#### View\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `alias` | alias | yes | Stable view alias, unique within the entity |\n| `label` | text | yes | Display name of the view |\n| `description` | text | no | The view\'s description, written onto the view in the workspace |\n| `columns` | list of [View column](#view-column) (at least one) | no | The columns the view shows, in this order and no others; absent, every field |\n| `filters` | [filter](#views) | no | The rows the view keeps |\n| `sort` | [sort](#views) | no | The order the view reads its rows in |\n| `summary` | map of text \u2192 text | no | Field alias \u2192 the operation its footer cell states |\n| `frozen_columns` | integer \\| `null` | no | How many leading columns stay in place while the rest scroll |\n\n#### View column\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field_alias` | alias | yes | A field of this entity |\n| `visibility` | `"visible"` \\| `"hidden"` | yes | Field visibility state: \'visible\' = shown to everyone, \'hidden\' = not shown by default but members can toggle |\n| `width` | number | no | The column\'s width, in pixels |\n\n<!-- generated:end views -->\n\nA filter is a group \u2014 `{ "node_type": "group", "logic": "and" | "or",\n"children": [ \u2026 ] }` \u2014 or one condition on its own, `{ "node_type":\n"condition", "type": "select", "field_key": "tier", "operator": "has_any_of",\n"value": ["gold"] }`. A sort is a list of `{ "field_key": \u2026, "order": "asc" |\n"desc" | null }`. A condition\'s `type` is the field\'s type and its `operator` is\none that type admits \u2014 `has_any_of` / `has_none_of` / `has_all_of` / `is_empty` /\n`is_not_empty` for a select, `equals` / `greater_than` / `less_than` for a\nnumber, `on` / `before` / `after` / `between` for a date, `contains` /\n`is_any_of` for text. A select condition\'s `value` names option ALIASES.\n\n<!-- generated:start rules-view -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `view.fields` | A view\'s columns, summary, sort and filters name fields of its table. |\n\n<!-- generated:end rules-view -->\n\n## Groups and templates\n\nA member group and a document template are made on their own, outside a table document and an app tree. A tree names a\ntemplate by its alias. A table document names a group by its id, where a `read_scope` clause (`member_of`),\na stage owner (`group`) or a check\'s `member_of("<id>")` takes one. `get_workspace` kind `group` lists the groups with their ids and members; `set_group` creates,\nchanges or deletes one (an admin\'s verb).\n\n`{{name}}` in an `html` or `email` template is filled from the workflow\'s data. Inline content is refused where it holds a\nscript, a frame, a `<link>`, an inline event handler or a `javascript:` URL; an `html` one may be a fragment, kept as\nwritten and printed as a whole document.\n\nA template\'s markup is the `template` of `set_template`, as one JSON string. A paper shaped like a counterparty\'s \u2014 a letterhead, a reference line, a seal, a signature block \u2014 is an `html` template with a shell around the body; `document_templates` covers the types.\n\n**What an act\'s template is handed** is its record\'s own fields by alias: a\nselect, a member and a link as their words (a link as the linked row\'s title), a\nnumber, a date and a computed value that declares its kind raw (`2800000`,\n`2026-09-14`) and a files field as its file cells (`{id, name, mime, url}`, the photos in the order held; a child row\'s\nfiles likewise) \u2014 never a computed select. On one record it is\nhanded, too, the rows of each child List the record draws, listed under that child\'s\nlink\'s name for `{{#each <link>}}` (and under the child entity\'s alias too): every row filed under the record, oldest first, each\nby its own fields as the record\'s are, less its link back to the record. Over\nseveral rows the rows are listed under the table\'s alias, for\n`{{#each <alias>}}`, without their child rows.\n\n**Photos in a paper.** A file cell is drawn only if whoever makes the paper may read that file, by the rule the file\nproxy applies; a file they may not read is never drawn. html: `{{#each photos}}<img src="{{url}}">{{/each}}`, each\nphoto inline, only those the data\'s file cells carry (a url typed into a text field stays a link). Word:\n`{{IMAGE picture(cover, 8)}}` for one file cell, and `{{FOR p IN photos}}{{IMAGE picture($p, 8)}}{{END-FOR p}}` for\nmany. Excel: `{{cover | picture:6}}` as the whole cell, and `{{#each photos}}` with `{{this | picture}}` for many, each\nrow growing to its picture. The number is the width in centimetres (8 by default, 6 in Excel); the height follows the\npicture. `picture` takes one image file cell: a list, a file that is no image and a file that cannot be read are\nrefused by name. Photos are embedded at most 1600 pixels across.\n\nHelpers print a raw value in the template\'s `locale` \u2014 `vi` or `en`, the organization\'s language where it states\nnone; each prints `""` for an empty value:\n\n| Helper | Prints (`vi` / `en`) |\n|---|---|\n| `{{money total "USD"}}` | `1.250,50 US$` / `$1,250.50`, in the currency\'s minor units; no code, a number |\n| `{{number qty}}` | `1.234,5` / `1,234.5`: grouped, up to 2 decimals |\n| `{{date issued_on}}` | `14/09/2026`; a date-fns pattern as the second argument, `{{date at "dd/MM/yyyy HH:mm"}}`, literal text quoted (`"\'Ng\xE0y\' dd"`). A moment prints on the workspace\'s timezone |\n| `{{words total "USD"}}` | `M\u1ED9t ngh\xECn hai tr\u0103m n\u0103m m\u01B0\u01A1i \u0111\xF4 la M\u1EF9 n\u0103m m\u01B0\u01A1i xu` / `One thousand two hundred fifty US dollars and fifty cents`; no code, VND (`\u0111\u1ED3ng`) |\n| `{{qr link}}`, `{{code128 position 6}}` | the value as a QR code, or as a Code 128 barcode (ASCII text only), a picture the width of the optional second argument in centimetres, by default sized to the value (a module prints 0.5 mm in a QR code, 0.33 mm in a barcode); a width too narrow to scan is refused, naming the least that does; not in an email template |\n| `{{date (add_days issued_on -1)}}` | `13/09/2026`: the date shifted by whole days (`-1`: the day before), for `date` to print; a number field as the days, empty shifting none |\n\nA value it cannot read (`{{money "abc"}}`, `{{date "14/09/2026"}}`) fails the render. A field named like a\nhelper and written alone (`{{number}}`) prints its own value.\n\nA field printed as a code in the other types: Excel `{{position | qr}}` or `{{position | code128:6}}` as the whole\ncell and the last helper \u2014 the picture is anchored on the cell and its row grows to hold it; Word `{{IMAGE qr(position)}}` or `{{IMAGE code128(position, 6)}}`. An empty field draws no picture.\n\n## Rows\n\n`rows` of a `set_tables` call are the first records of the tables the call creates, keyed by each one\'s `ref`;\na patch of an existing table seeds none, so write its rows into the table. A sample, not a data set: rows past it go in with `create_records` once the tables exist, in the batches its description bounds.\n\n```jsonc\n"rows": {\n "customer": [\n { "ref": "acme", "fields": { "name": "Acme Trading", "tier": "gold" } }\n ]\n}\n```\n\n`rows` sits beside `tables` in the call, not inside a table.\n\n<!-- generated:start rows -->\n\n#### Row\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `ref` | text | yes | Local handle for this row, referenced by other rows\' link fields |\n| `fields` | map of text \u2192 any value | yes | Field alias \u2192 the value, read against the field\'s declared type |\n\n<!-- generated:end rows -->\n\n<!-- generated:start rules-rows -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `rows.ref` | A row\'s `ref` is unique within its table. |\n| `rows.required` | A row states every field its table requires: it is created with what it states, its fields\' defaults and copies unapplied. |\n| `rows.value` | A row\'s value fits its field: an option alias for a select, `"<table ref>:<row ref>"` of a row of the linked table in this call for a link, `"self"` for a member, a `fil_` id for files, a date in the date grammar (an hour only on a datetime), a text, number, yes/no or null otherwise \u2014 and none on a computed field. |\n\n<!-- generated:end rules-rows -->\n\nA `ref` is lowercase letters, digits and underscores, and is never persisted.\n\n`fields` is keyed by field alias, and every value is read against the field\'s\nDECLARED type:\n\n| Field type | Value |\n|---|---|\n| `text` / `number` / `boolean` | the value itself |\n| `date` | `"2026-03-14"`, or a relative expression (below) |\n| `select` | the option ALIAS \u2014 `"gold"`, or `["gold","vip"]` for a multi-select |\n| `link` | `"<table ref>:<row ref>"` naming another row of this call \u2014 `"customer:acme"`, or an array for several; a paired link is stated on ONE side (the child\'s link to its parent) and its partner fills itself |\n| `member` | `"self"` only \u2014 the member making the call |\n| `files` | the `fil_` id of a file already in this workspace, or a list of them |\n| `formula`, `rollup`, `lookup`, `autonumber` | not allowed \u2014 the platform writes these |\n\n### Relative dates\n\nA date cell holds a literal `YYYY-MM-DD`, or an expression relative to the day\nthe call is made, so a screen that opens on "this month" is not empty a month\nlater:\n\n- `@today` \u2014 the day of the run, in the workspace\'s timezone\n- `@month-start` \u2014 the 1st of that month\n- either with a whole-day offset: `@today-14`, `@month-start+9`\n- on a date that holds its hour (`format: "datetime"`), that hour after the day, and always stated:\n `@today 14:30`, `@today+1 06:00`\n\n`@month-start` exists because `@today-N` cannot promise a month: applied on the\n2nd, `@today-3` lands in the previous one.\n\n## Records\n\n`records` says how a row of a table is RECOGNISED \u2014 stated once in the table\'s document, beside its\n`title` \u2014 and every surface that draws one of its rows reads the same\nstatement: the register\'s row, a picker\'s option, a link\'s chip, a child table\'s\nrow and the record\'s header. Every table an app lists, opens or picks has one. The rarer keys \u2014 `scan`, `cadence`, `place`, `on_site`, `pin`, `plan_place` and `size` \u2014 are stated on `model/record-places-pins-and-cadence`.\n\n```jsonc\n// table "visit"\n"title": "container_no",\n"records": {\n "subtitle": ["customer", "arrived_on"],\n "image": "photos",\n "status": { "field": "stage", "closed": ["gone"], "history": "visit_history" },\n "figure": "total_fees",\n "due": ["free_until"]\n}\n```\n\n```jsonc\n// table "release": a release order read against what it allows \u2014 a meter wherever it shows\n"title": "release_no",\n"records": { "figure": "issued", "limits": { "issued": "units" } }\n```\n\n```jsonc\n// table "dossier": a straight-line process \u2014 the stage a select, each step\'s day the date it stamps\n"title": "applicant",\n"records": { "status": { "field": "stage", "closed": ["signed"], "dates": { "received": "received_on", "appraised": "appraised_on", "signed": "signed_on" } } }\n```\n\n<!-- generated:start records -->\n\n#### Record\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `subtitle` | list of alias (1\u20132) | no | Up to two fields read under the title wherever a row is drawn (a code, a date, a party): one line at a desk, wrapping to a second on a phone |\n| `image` | alias | no | A files field that pictures the row \u2014 never one an act keeps the document it makes in |\n| `mark` | alias | no | A single select whose option leads the row wherever a mark leads it \u2014 a list\'s row, the record\'s head, a link naming the row: the option\'s brand or glyph, else its colour dot, on a tile. The field stays a field: a subtitle run, a column, a section\'s control. Never beside `image` or `party`, and never the status field |\n| `description` | alias | no | A text field saying what the row is about \u2014 a plan\'s brief, a customer\'s note: read in its record\'s head under the title\'s line, its first lines at rest and whole on a press, and written from the head\'s \u270E. Never on a row\'s line |\n| `party` | `"person"` \\| `"organization"` | no | What each row is where the rows are people (a contact, a patient) or organisations (a customer, a supplier, a carrier); absent, the rows are things (an order, an item, a paper) |\n| `status` | [Status](#status) | no | The stage a row moves through, a single select, drawn as a badge beside its title |\n| `figure` | alias | no | The one number the row stands for, read at its right (a total, a quantity left) |\n| `limits` | map of alias \u2192 (alias \\| number) | no | A number read against a bound \u2014 a field of the same row or a constant \u2014 drawn as a meter wherever it shows |\n| `gates` | map of alias \u2192 (alias \\| number) | no | A pass mark on a meter `limits` bounds \u2014 a field of the same row or a constant; a percent field is its share of the bound, any other number an amount. The meter marks it and fills complete once past it |\n| `tolerance` | map of alias \u2192 number | no | How far past its `limits` bound a meter may stand, in percent of the bound: the bound is an amount expected (received against ordered), not a cap. Without one the bound is a cap, and past it is an overrun |\n| `due` | list of alias (at least one) | no | Dates that are deadlines (stored, or computed as a date): each counts down and turns overdue |\n| `frees` | alias | no | A date ending the row\'s span of days on which what the row holds is free again (a check-out, a hire\'s return): a stay from the 3rd to the 5th holds the 3rd and the 4th, and another may start on the 5th \u2014 on a lanes board, a calendar, a roster and in `no_overlap` alike. Absent, a span of days holds its last day; a span of moments always frees its end |\n| `settled` | alias | no | A yes/no of the row (stored, a formula: an invoice paid in full, or looked up through a one-row link: a line settled with its parent) that ends its deadlines: while it is true, no `due` of the row counts down or turns overdue \u2014 in a cell, on its line, in its head, on a calendar or gantt, or in the overdue count. A dashboard\'s or reading\'s `where` reads it as any other yes/no |\n| `task` | `true` | no | A row is work someone finishes: wherever rows of it stand in a table \u2014 a register, a record\'s rows, the rows filed under one \u2014 each is led by a ring ticking it done and unticking it by the one write of the app making that move. Its status is a stored select with `closed` |\n| `applies` | map of alias \u2192 map of alias \u2192 (list of alias (at least one) \\| boolean) | no | Fields that apply only while their conditions hold (a length only on a line whose tariff charges by the metre): where one does not apply no add asks it and no surface shows it. Never the title; a required one starts at its `default` |\n| `starts` | map of alias \u2192 `"today"` \\| `"me"` | no | What a field of a row being added starts at, where that start is certain: `today` a date (a moment starts now), `me` a member, the reader adding it; every other field starts empty unless the reader narrowed the register to a value or the field declares a `default` |\n\n<!-- generated:end records -->\n\n<!-- generated:start rules-records -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `records.title` | A title is the one field naming the row \u2014 any field but a button or a yes/no \u2014 and the subtitle never names the title\'s own field. |\n| `records.image` | `image` is a files field, never one an Act keeps the documents it makes in (`into`). |\n| `records.mark` | `mark` is a single select, never the status field, and never beside `image` or `party` \u2014 a List keeps one lead slot \u2014 nor on the folder an App works inside (`scope`), which its switcher names by a picture or its initials. |\n| `records.description` | `description` is a text field, read in the record\'s head \u2014 never a Field of a part too. |\n| `records.figure` | A figure is a number. |\n| `records.figure-on-line` | *Noted, never refused:* A subtitle entry equal to the figure is shown once, as the headline; the line leaves it out. |\n| `records.applies` | `applies` holds a field of the row other than its title to conditions of the same row \u2014 a yes/no (stored or a formula) by `true` or `false`, a single select by its options, its own or looked up through a one-row link \u2014 never to itself; a required field it holds starts at a `default`. |\n| `records.limits` | `limits` bounds a number by a number field of the row, in its unit or one of the same dimension \u2014 or by a constant, where every row reads the figure in one unit. |\n| `records.gates` | `gates` marks a pass on a meter `limits` bounds: a percent field (its share of the bound), another number in the bound\'s unit, or a constant. |\n| `records.tolerance` | `tolerance` stands on a meter `limits` bounds. |\n| `records.starts` | `starts` starts a field a person writes \u2014 `today` a date or a moment (never a span), `me` a member \u2014 and a `create` that does not ask a field it starts writes that start. |\n| `records.due` | A deadline (`due`) is a date. |\n| `records.frees` | `frees` names a stored day, never a moment: a span of moments already frees its end. |\n| `records.settled` | `settled` names a yes/no of the row (stored, a formula, or looked up through a one-row link) beside a `due`: while it holds, the row\'s deadlines neither count down nor turn overdue. |\n| `records.task` | A task entity (`task`) states a `status` of a stored single select with `closed` options \u2014 its ring ticks a row done by moving it to one. |\n| `records.picture` | *Noted by the designer on a write, never refused:* Rows an App draws that each hold one files field no Act makes documents into, no call is filed into, no files gallery keeps and no List compares as a pair (`compare`) \u2014 and no figure \u2014 are pictured by it (`image`); where the workspace\'s files are known, the field holding images is the picture, a figure or not. |\n\n<!-- generated:end rules-records -->\n\n- **The title names the row** by who or what it is \u2014 a link by the linked row (a job\n read by its customer). Any field but a button or a yes/no may title it, yet a code\n titles a row only where people name the row by it, else it is a `subtitle`; it is the table\'s own setting, which every link shows.\n- **One lead per row.** A row leads with its picture (`image`), a face (`party`) or the\n option of a single select it holds (`mark`) \u2014 never two. A `mark` draws that option\n on a tile: its brand or glyph, else its colour dot; a row holding none, a ring saying\n none is picked. The same tile heads the row\'s record and leads it where a link\n names it; the select stays a field \u2014 a subtitle run, a column, a section\'s control.\n- **A limit** reads a number against a bound \u2014 another field of the row, or a constant.\n A `gates` entry marks a pass on that meter: a percent field is its share of the\n bound, any other number an amount.\n- **A deadline** (`due`) counts down wherever it is drawn and is overdue once past while\n the work is open. A register over the table says how many rows in view are overdue on\n its summary line, and pressing that count narrows to them \u2014 no reading to state.\n- **A span\'s free day** (`frees`) is the end date on which what the row held is free\n again \u2014 a check-out, a hire\'s return. A stay from the 3rd to the 5th holds two nights:\n a lanes board, a calendar and a roster draw it through the 4th, and `no_overlap` lets\n the next stay start on the 5th. Without it a span of days holds its last day; a span of\n moments always ends as the next may begin.\n- **`starts`** is where an add\'s field begins, stated only where that is certain \u2014 a\n log\'s own day at `today`, a request\'s requester at `me`. Nothing else starts a field\n but its declared `default` and what the reader narrowed the register to.\n\n## Record places, pins and cadence\n\nThe rarer keys of `records`, stated beside the ones on `model/records`: a title read off a photo (`scan`), the rows a child owes each day (`cadence`), where a row stands on a map (`place`, `on_site`) or on a picture (`pin`, `plan_place`).\n\n```jsonc\n// table "site": it owes a diary page each day it works, from breaking ground through handover\n"title": "name",\n"records": { "cadence": [{ "entity": "diary_page", "on": ["mon", "tue", "wed", "thu", "fri", "sat"], "from": "started", "until": "handed_over" }] }\n```\n\n<!-- generated:start records-places -->\n\n#### Record places\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `cadence` | list of [Cadence](#cadence) (at least one) | no | The rows of a child each row owes one of a day, on the days of the week listed (a site\'s diary page, Monday to Saturday) \u2014 one entry a child, never owed on a day still to come. A roster of these rows over days marks a past day owed with none missing in the danger tone, counted in the legend; today\'s is due, offered to add and never marked; each row\'s total reads the days it holds of those owed |\n| `scan` | `"text"` \\| `"iso6346"` \\| `"code"` | no | Its title, a text field, is painted or printed on the thing a row is (a container\'s number, a seal\'s) and read off one photo on a phone, beside each search matching it and each field holding or picking it: `iso6346` a container number, refused unless its check digit holds; `text` as it stands; `code` a barcode or QR code on the thing, decoded on the device from the photo with no agent |\n| `place` | [Place](#place) | no | Where a row stands, as plain fields of it, drawn as one place where its record draws the address: how sure it is, a press opening it in the device\'s maps, and one taking it from where the device stands |\n| `plan_place` | [Plan place](#plan-place) | no | Each row is a place on the plan a `plan` register draws: its title the place\'s code, its subtitle a note. A flat list, no tree |\n| `on_site` | [On site](#on-site) | no | Photos taken where a place stands (a site diary\'s): a photo taken through a phone\'s camera here keeps where the device stood, marked at the place, away from it or unknown, and a record holding one taken away says so among its checks \u2014 one with no place (picked on a computer, or kept before places were) is marked and warns nothing; a place reaches only members of the organization, through the apps drawing these photos |\n| `pin` | [Pin](#pin) | no | Each row is a place on a picture (a snag on a flat\'s plan, a spot on a container\'s drawing): a List of these rows draws the picture its `on` row holds with a numbered pin at each row\'s own place, each row led by its number, the row opened lit on it and a pin pressed opening its row; where the List writes rows, a press on the bare picture adds one there, and a pin dragged moves its row. A List of rows that link to these by a single link draws their places the same way |\n| `size` | [Record size](#record-size) | no | The real size of the thing a row is (a carton, a piece of furniture, a pallet): three number fields stating one length unit alike, read as one fact \u2014 "400 \xD7 300 \xD7 250 mm", a part the row lacks "\u2014" \u2014 after the subtitle wherever the row\'s line and head draw facts, never also as the subtitle\'s fields. A plan register drawing these rows draws each box at this size |\n\n#### Cadence\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `entity` | text | yes | The table owed: each of its rows stands on one row of this table by its single link to it, and on the first date of its line (`records` title or subtitle) |\n| `on` | list of `"mon"` \\| `"tue"` \\| `"wed"` \\| `"thu"` \\| `"fri"` \\| `"sat"` \\| `"sun"` (at least one) | yes | The days of the week one is owed on, each listed \u2014 `mon` to `sun` |\n| `from` | alias | no | A date of this row: the first day one is owed (a site breaking ground, a contract\'s start); absent, no first day |\n| `until` | alias | no | A date of this row: the last day one is owed (a site\'s handover, a contract\'s end); empty or absent, through today |\n\n#### Place\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `address` | alias | yes | A text field: the address, as people write it |\n| `latitude` | alias | yes | A number field: degrees north, negative south |\n| `longitude` | alias | yes | A number field: degrees east, negative west |\n| `accuracy` | alias | no | A number field: how many metres off the device that gave the place said it may be |\n\n#### On site\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `photos` | list of alias (at least one) | yes | Files fields of this row holding photos taken on site |\n| `place` | alias | no | A single link of this row to the row whose `place` its photos are taken at (a diary page\'s site); absent, this row\'s own `place` |\n| `radius` | number | yes | How many metres from the place a photo still counts as taken there |\n\n#### Pin\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `on` | alias | yes | A single link of this row to the row whose picture (its `records` `image`) the place is on \u2014 a container\'s drawing, a floor\'s plan |\n| `x` | alias | yes | A number field of this row: how far across that picture the place stands, in percent from its left edge |\n| `y` | alias | yes | A number field of this row: how far down that picture the place stands, in percent from its top edge |\n\n#### Record size\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `across` | alias | yes | A number field: the width of the thing a row is, across |\n| `along` | alias | yes | A number field: its depth, along |\n| `up` | alias | yes | A number field: its height, up |\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `records.scan` | A title read off a photo or a code (`scan`) is a text field the row stores. |\n| `records.cadence` | A `cadence` names each child once \u2014 its rows standing on one row of this entity by their single link and on the first date of their line \u2014 lists each day of the week once, and `from` and `until` are two different days of this entity. |\n| `records.place` | A `place` names four different fields of the row: its address a text field, its latitude, longitude and accuracy number fields it stores. |\n| `records.plan_place` | A `plan_place` names `kind` and `reach_from` as single selects, `sheet` as a single select or a one-row link, `stacked` as a boolean and the rest as numbers, each a different field; `kind`, `x`, `y` required. `size` names three numbers (`across`, `along`, `up`) stating one cell in one length `unit` alike. A `sheet` linking to an entity that states an `image` draws that drawing under the places, `scale` (a number field of that entity) its width in cells; `scale` goes with such a `sheet` alone. |\n| `records.on_site` | `on_site` lists files fields of the row, each once, taken at the `place` of the row itself or of the one row a single link of it names (`place`), which states one. |\n| `records.pin` | A `pin` names a single link of the row (`on`) to an entity whose `records` states its `image` (a PDF is drawn as its first page), and two different number fields of the row, how far across (`x`) and down (`y`) that picture in percent. A List of rows whose own entity states a `pin` is drawn over that picture; `on`, `x` and `y` are set by the press that adds a row, never asked. |\n| `records.size` | A `size` names three different number fields of the row (`across`, `along`, `up`) stating one length unit alike (mm, cm, m or km); they read as one fact after the subtitle, so the subtitle never also names one. |\n\n<!-- generated:end records-places -->\n\n## Status\n\nA record\'s `status` is the stage a row moves through \u2014 a stored single select, each\noption\'s moment the date `dates` names \u2014 drawn as a badge beside its title wherever the row is.\n\n<!-- generated:start status -->\n\n#### Status\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The single select that is the row\'s state, its options in the order the work goes through them |\n| `closed` | list of alias (at least one) | no | Options that end the work: a register opens on the rows not at them, and the badge reads muted |\n| `dropped` | list of alias (at least one) | no | Options of `closed` that leave the work undone (cancelled, rejected): whoever holds the row moves it to one from any stage, where every other move forward passes each owned stage between that binds the row (its `when`) and needs each one\'s owner, and a row leaving one enters a stage as a row made there does |\n| `history` | text | no | A table every write that sets or moves the status appends one row to (its link to this table, the new status, the moment and who) \u2014 by the record write path, whoever writes: for reporting on every move |\n| `dates` | map of alias \u2192 alias | no | Option alias \u2192 a date field of this entity the record write path stamps with the moment a row moves into that option, or is made at it \u2014 whoever writes. A value the same write states for that field wins, so past data loads as it happened; the platform never clears a stamp, so each is when the row last entered the option. A stage\'s date is made where none is named |\n| `stages` | map of alias \u2192 [Stage](#stage) | no | Option alias \u2192 who owns that stage and how long it should take: the owner alone takes a row out of it, by any write, and presses the acts offered there; every move forward past it of a row it binds (its `when`), and every such row made past it, is its owner\'s too. Its days run from the moment its `dates` field holds. In an app the status then moves only by acts that `set` one of its options \u2014 no save writes it and no act asks it |\n\n#### Stage\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `owner` | text \\| [Stage group](#stage-group) | yes | Who owns the stage: a member field of the row, `link.field` read through a one-row link (not at the stage a row opens at, whose owner made the row: a member field of the row itself), or `{ group }` |\n| `within` | integer | no | Whole days the stage should take from the moment the row entered it |\n| `when` | map of alias \u2192 (list of alias (at least one) \\| boolean \\| [comparison](#values) \\| `{ "empty": true \\| false }`) | no | The rows that must pass this stage: a row meeting every entry on its own fields, as it stood before a write or as the write leaves it \u2014 a single select holding one of these options, a yes/no this value, a number or date meeting its bounds, a field empty or filled. Absent, every row must. A row at the stage leaves it by its owner either way |\n\n#### Stage group\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `group` | text | yes | A member group\'s id (from Settings, or chat): every member of it owns the stage |\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `records.status` | A status is one single select, its options in the order the work goes through them, `closed` naming the ones that end it and `dropped` those of them reached from any stage. |\n| `records.history` | A declared status `history` has exactly one link to the row it records, one single select holding every option of the status (or a text), exactly one date and at most one member. |\n| `records.stages` | A stage\'s owner (`status.stages`) is a single member field of the row, one read through a one-row link (never at the stage a row opens at: its owner made the row, so it is a member field of the row itself), or a group, on an option that does not end the work; a group\'s stage needs the status `history` with who moved the row. Every write takes a row off a stage as its owner alone, and moves a row past it or makes a row past it as its owner alone where the stage binds the row (its `when`) \u2014 a move to a `dropped` option excepted, which whoever holds the row makes from any stage; a row leaving a dropped option, or holding none, enters a stage as one made there. Each stage\'s moment, and each open option\'s, is the date its `dates` names, made where none is named, which every write that opens or moves the status stamps; `within` runs from it. In an App a status with stages moves only by Acts that `set` one of its options \u2014 no save writes it or a date it stamps, and no Act asks either, sets it otherwise or writes either from a workflow; an Act moving a row forward past a stage another owner owns, into an option not `dropped`, keeps its `when` off every row that stage binds. |\n| `records.stage-when` | A stage\'s `when` names the rows that must pass it, as an Act\'s `when` holds them \u2014 single selects to their options, yes/nos to `true` or `false`, numbers and dates to bounds, any field to `{ "empty": true \\| false }` \u2014 on fields a write stores of the row itself: never the status, a formula, a lookup or a rollup, and never on the stage a row opens at. It binds a row meeting it as it stood before a write or as the write leaves it; absent, it binds every row. |\n| `records.detour-note` | *Noted, never refused:* A stage the work only steps aside into \u2014 not where a row starts, entered from one step alone that also moves the work on without it, and left by its own moves only back to that step (a pause, a repair) \u2014 is a detour: its option and the Section whose `at` holds it stand before that step\'s, else the work reads it as its next step, and the Act moving the work into it leads that Section \u2014 never a correction waiting in a \u22EF. |\n\n<!-- generated:end status -->\n\n- **The status** is one single select. `closed` options end the work: a register opens\n on the rows not in them. A `history` table gets one row per move \u2014 its link to this\n table, the new status (a select holding the same option aliases, or text), the\n moment and who \u2014 appended by the record write path in every write that opens a row at a\n stage or moves it, whoever writes (the member behind the write is its `by`; an automation\n leaves it empty). A `history` the tables do not declare is derived, and needs no\n `records` of its own.\n- **When each step happened** is the date `dates` names for its option, stamped as the row\n enters it, whoever moves it. One way times a status: a straight-line process is a select\n whose options are its steps in order, each with its date \u2014 never a run of dates the stage\n is read off. An Act that moves the stage and `asks` the step\'s date with `starts: "today"`\n lets the reader record a day already past in the same press.\n\n## Write rules\n\nWhat a WRITE of a table\'s rows meets is stated in the table\'s document, where it applies: `min` and\n`max` on a number or date field, `options_where` and `same` on a link, `suggest` on a text field,\nand `no_overlap` and `natural_key` on the table. A key a field\'s type does not take is refused at\nits path, and a document has no `write_rules` key.\nEvery one but `natural_key` and `suggest` holds on every path a record is written by \u2014 the grid, a record\'s form, an import, a workflow, an\nagent, the API, an app \u2014 judged in the write\'s own transaction, every broken rule listed at once; the\nscreen and a picker only mirror them. A row that already breaks one stays editable until a write\ntouches what the rule reads. `natural_key` names the fields an app recognises a row by, to reuse it where an\nadd or a find-or-create meets it; a record tool or the API creates the row it is handed. `suggest` offers\nvalues in a text control and refuses none. An `options_where` link is refused where a write names a row the\nnarrowing does not admit, and every picker of the link offers only the rows it admits.\n\n```json\n{\n "tables": [\n {\n "ref": "customer",\n "label": "Customer",\n "fields": { "email": { "type": "text", "label": "Email", "unique": true } },\n "natural_key": ["email"]\n }\n ]\n}\n```\n\nA customer above is RECOGNISED by their address. An add that names one \u2014 from the customers\nregister or from a picker\'s "new" \u2014 reuses the row it matches and opens one only where nothing does,\nso the book never grows a second Acme.\n\n```json\n{\n "tables": [\n { "ref": "product", "label": "Product", "fields": { "name": { "type": "text", "label": "Name" }, "in_stock": { "type": "number", "label": "In stock" } } },\n {\n "ref": "order",\n "label": "Order",\n "fields": {\n "name": { "type": "text", "label": "Name" },\n "placed_on": { "type": "date", "label": "Placed on" },\n "due_on": { "type": "date", "label": "Due on" },\n "lines": { "type": "link", "label": "Lines", "table": "order_line", "multi": true, "pair": "order" }\n }\n },\n {\n "ref": "order_line",\n "label": "Order line",\n "fields": {\n "name": { "type": "text", "label": "Name" },\n "order": { "type": "link", "label": "Order", "table": "order", "multi": false, "pair": "lines" },\n "quantity": { "type": "number", "label": "Quantity", "min": 1, "max": 9999 },\n "ships_on": { "type": "date", "label": "Ships on", "min": "order.placed_on", "max": "order.due_on" },\n "product": {\n "type": "link",\n "label": "Product",\n "table": "product",\n "multi": false,\n "options_where": {\n "node_type": "group",\n "logic": "and",\n "children": [{ "node_type": "condition", "type": "number", "field_key": "in_stock", "operator": "greater_than", "value": 0 }]\n }\n }\n }\n }\n ]\n}\n```\n\nA line of nothing is not a line: `quantity` is refused on create and on update. A line ships inside\nits order\'s window \u2014 a bound read off the row a link names is judged when the line is written. Nothing is sold off an empty shelf: every picker of `product`\nreads only the rows that answer its `options_where`, and every save refuses the same rows again.\n\n```json\n{\n "tables": [\n {\n "ref": "booking",\n "label": "Booking",\n "fields": {\n "room": { "type": "text", "label": "Room" },\n "state": { "type": "select", "label": "State", "options": [{ "alias": "held", "label": "Held", "color": "blue" }, { "alias": "released", "label": "Released", "color": "gray" }] },\n "starts_on": { "type": "date", "label": "Starts on" },\n "ends_on": { "type": "date", "label": "Ends on", "min": "starts_on" }\n },\n "no_overlap": { "from": "starts_on", "to": "ends_on", "by": ["room"], "while": { "state": ["held"] } }\n }\n ]\n}\n```\n\nOne booking per room at a time, while it is held \u2014 a create or a save whose span overlaps another\nheld booking of the same room is refused.\n\nA table\'s `checks` (the conditions no write of its rows may meet) and `workflows` (what follows every committed write of them) are stated in its document and hold on every path a record is written by; their keys, what they read and an example are on `workflows/table-checks` and `workflows/table-workflows`.\n\n<!-- generated:start write-rules -->\n\n#### Suggest\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `table` | text | yes | The table whose text field is offered from: its id, or its `ref` in this call |\n| `field` | alias | yes | The alias of that table\'s stored text field; the field\'s control offers, as the reader types, the distinct values it holds, and any text is still written |\n\n#### No overlap\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `from` | text | yes | The stored date (or datetime) a row\'s span starts on: `<field alias>` of the row, or `<link alias>.<field alias>` of the one row a one-row link names (a booking line\'s piece held for its booking\'s dates) |\n| `to` | text | yes | The stored date (or datetime) a row\'s span ends on, named like `from` and through the same link \u2014 a day it holds, unless `records` names it the day the row `frees`; a moment another row may start at |\n| `by` | list of alias (at least one) | no | Fields two rows share to compete for a span (the same employee, the same room); absent, every row |\n| `while` | map of text \u2192 list of alias (at least one) | no | Only rows at these options of these selects hold their span (running, not closed), the select named like `from` \u2014 `<field alias>` or `<link alias>.<field alias>`; absent, every row |\n\n<!-- generated:end write-rules -->\n\n<!-- generated:start rules-write -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `write.natural-key` | A `natural_key` is text or a number a person types back, or a one-row link; a text key is unique \u2014 `unique: true`, or the key\'s fields as a set in the table\'s `unique`. |\n| `write.bounds` | `min` and `max` bound a number or a date \u2014 by a constant a number, by a field of this row or of the one row a one-row link names either, of the same type and unit, never by itself \u2014 and `min` is never above `max`. |\n| `write.bound-per-row-unit` | On a figure whose unit or currency is the row\'s own (its notation\'s `per_row`) a constant bound is only 0 \u2014 bound it by a field of the row. |\n| `write.suggest` | `suggest` stands on a stored text field and names a `table` and a `field`, a stored text field of that table; the app reads that table, so it has a `records` entry. |\n| `write.same` | `same` stands on a link and names one-row links this table and the link\'s target both hold to one table. |\n| `write.options-where` | `options_where` narrows a link by an `and` group of plain conditions over the target\'s own fields \u2014 a number compared in one unit on every row, a select by options it declares. |\n| `write.no-overlap` | `no_overlap` spans two date fields, the row\'s own or the one row a one-row link names (`booking.start_on`); `by` names values rows share \u2014 a link, a person, one option, a text or a number; `while` names single selects by their options, the row\'s own or that linked row\'s. |\n\n<!-- generated:end rules-write -->\n\n- A `natural_key` is `text` or `number`, because a person types it back, or a\n one-row link, because a row is also recognised by the one row it belongs with (a\n fee by its visit and its kind). A key holding a `text` field is unique: `unique:\n true` on that field, or the key\'s fields as a set in the table\'s `unique` (a\n site by its customer and its name) \u2014 two rows sharing it would make\n find-or-create pick whichever the read answered first. An add asks the key by\n default.\n- A `same` link names only rows that name what this row names \u2014 a fee\'s invoice\n is one of its own visit\'s \u2014 through a one-row link both tables hold to one\n table; its picker offers nothing until the row names one. A row added under a\n record names what it copies off it (`default_from`) from the start, so a block\n expecting one line per row of the link lists that record\'s rows alone. A save\n of this row moving what the two compare is refused while they would name apart.\n- An `options_where` link may be left empty; a row it names is refused on every\n save that writes the link unless it holds the narrowing, and the refusal names\n the link and the row. A save that leaves the link alone is not judged, so a row\n still naming a row the narrowing no longer admits stays editable. A link naming\n one kind of a book\'s rows (a "Shipping line" among the parties) narrows to that\n kind, or its picker offers every row.\n\nA field\'s own `required` is not here: the field carries it, and every write\npath refuses the row by field name from it. `unique` is the text field\'s own\nclause (\xA7 `text`) \u2014 an add says so at the control before the column does. A\nfield the workspace writes (a formula, a rollup, a lookup, an autonumber, a date\nwith `derive_from`) is never asked, written or edited.\n\n## App tree\n\nAn app is a tree of components, assembled the way a page is written in JSX: pick a component, bind it to a table by\nname, and it draws well with no props, because its defaults come from the table\'s facts (\xA7 Records, \xA7 Status,\n\xA7 Write rules, \xA7 Field). A prop overrides a default at its own node. No prop says how anything looks: the runtime\ndraws each concept one way, and a screen the job needs that no component states is told to the person \u2014 the job,\nwhat the app drew, the word wanted \u2014 never props bent to approximate it.\n\nEach component is a page of its own: `model/app-node`, `model/list-node`, `model/record-node`, `model/section-node`,\n`model/side-node`, `model/page-node`, `model/outside-node`, `model/tab-node`, `model/field-node`, `model/act-node`, `model/check-node`,\n`model/reading-node`, `model/progress-node`, `model/statement-node`, `model/export-node`, `model/book-node`,\n`model/query-node` and `model/workflow-node`. Beside them: `model/changing-an-app` and `model/what-an-app-may-write`, `model/layouts`, `model/time-and-place-layouts` and\n`model/publish-desk` for a List\'s `layout`, `model/composed-page`, `model/tasks`, and the rules, `model/rules-of-the-app`,\n`model/register-rules`, `model/block-rules` and `model/record-and-act-rules`.\n\n<!-- generated:start app-components -->\n\n| Component | Draws | Holds |\n|---|---|---|\n| [App](#app-node) | the launcher tile, the app\'s words, its Lists, and its API | List, Tab, Reading, Query, Workflow |\n| [List](#list-node) | rows of an entity: the register, a record\'s child rows, a roster\'s cells, related rows, a log | Tab, Reading, Progress, Export, Act, Check, Book, Record, List |\n| [Record](#record-node) | what a row opens: the head and the parts | Field, Section, Side, Page, Outside, List, Reading, Progress, Statement, Act |\n| [Section](#section-node) | a titled part of a record, and optionally a step of its path | Field, List, Reading, Progress, Statement, Act |\n| [Side](#side-node) | the part the rest is worked from, beside them | Field, List, Reading, Progress, Statement, Act |\n| [Page](#page-node) | a part on its own page, off the record\'s scroll | Field, List, Reading, Progress, Statement, Act |\n| [Outside](#outside-node) | what one record shows, and which acts it allows, to a person with no seat who opens a link a member made of it | Field, List, Act |\n| [Tab](#tab-node) | one view of a List\'s rows over a period, or one sheet of a dashboard | Reading, List |\n| [Field](#field-node) | one field: a control, a gallery, prose, a link and its picker, the rows filed under a back-link | \u2014 |\n| [Act](#act-node) | a verb: what pressing it does | \u2014 |\n| [Check](#check-node) | a formula of the rows that warns, or refuses the writes and acts it names | \u2014 |\n| [Reading](#reading-node) | a number over rows: one figure, its parts, a grid, per period, steps; or a record\'s own figure | \u2014 |\n| [Progress](#progress-node) | the work against its plan, day by day | \u2014 |\n| [Statement](#statement-node) | a figure built from figures of the record | \u2014 |\n| [Export](#export-node) | a report file of the rows in view | \u2014 |\n| [Book](#book-node) | another table read as part of the register | Record, Act, Check |\n| [Query](#query-node) | nothing: a read for agents | \u2014 |\n| [Workflow](#workflow-node) | nothing: a verb for agents | \u2014 |\n\n<!-- generated:end app-components -->\n\n### A node\n\nA node is `{ "type": <Component>, "key"?, ...props, "children"?: [...] }`. `type` names the component, `key` its\nname among its siblings, the other keys its props, `children` the nodes it holds. An unknown prop is refused.\n\n- **Two shorthands, both for Field.** A bare string child is a Field (`"due_date"`); an object with `field` and no\n `type` is a Field with props (`{ "field": "price", "edit": false }`). Both work in every prop that lists fields:\n `create`, `row.subtitle`, `add`, and the `children` of a Record or a part. A column is a field\'s alias, or\n `{ "field", "edit": true }` for the line\'s own control, and takes no other prop.\n- **Fields and options are named by alias**, never by label. A tagged value is an object, never a magic string.\n- **`false` turns a default off at its node:** `"figure": false` drops the row\'s figure, `"edit": false` makes a\n field read-only, `"create": false` and `"remove": false` take a List\'s add and Delete away, `"readings": false` its\n band, `"row": { "status": { "edit": false } }` the status buttons (another app moves the stage) and\n `"row": { "image": false }` the picture.\n\n### Keys and addresses\n\nA key is unique among its siblings, the way a React key is. A node that binds data takes its key from what it binds:\n\n| Node | Key when none is stated |\n|---|---|\n| Field, Check | its field alias |\n| List, Book | its entity alias |\n| Record | `record` |\n\nEvery other node states its key. Three kinds of key are unique across the whole app, because each is an API name:\nan Act\'s (its workflow is `act_<key>`, or the one it names), a Reading\'s (its query name for `run_app_query`), and a Query\'s or\nWorkflow\'s.\n\nA node\'s **address** is the path of keys from the app, such as `customer/record/orders`; the app itself is `/` (the `in` of a node at the root), and its own key is part of no address. A patch may use any ending\nof that path that names exactly one node (`orders`). The exact path wins where one exists; an ending naming two\nnodes is refused, and the refusal lists both paths. `get_app` prints the tree with each node\'s address.\n\n### Values\n\n| Value | Written |\n|---|---|\n| now, the reader, an asked input | `{ "now": true }`, `{ "me": true }`, `{ "input": "name" }` |\n| a computed value | `{ "formula": "{cost} * 1.2" }` over the acted row\'s fields |\n| a call\'s own value, in a Query | `{ "param": "name" }` |\n| cleared | `null` in an Act\'s `set` |\n| an option | its alias |\n\nA **condition** (`when`) maps a select to a list of its option aliases and a yes/no to `true` or `false`; every entry\nholds. A number (a number, currency or percent field, or a formula or rollup yielding one) takes a comparison with any\nof `gt`, `gte`, `lt`, `lte` (at least one, never both `gt` and `gte` or both `lt` and `lte`), each a number; a date\ntakes the same keys with a day (`"2026-03-01"`), a moment on a datetime (`"2026-03-01T09:30"`) or `{ "now": true }`\n\u2014 today, or the current minute on a datetime, read when the rows are, so overdue is `{ "due_on": { "lt": { "now": true } } }`;\nand any field but a yes/no takes `{ "empty": true }` or `{ "empty": false }`. A comparison on a field it does not fit is\nrefused, naming the field, its type and what it takes. A `when` on an Act, a Section or other block, and a List\'s `where`\n(the register\'s or a child List\'s) take these; a child List that adds, picks or expects rows refuses a comparison in its\n`where`, since a row it holds could stand outside it. A **narrowing** (`opens`, a Reading\'s, a Tab\'s or an Export\'s `where`) takes options, yes/nos and also `{ "me": true }` on a member\nfield, the reader; `where` on the register, a log or a record\'s child rows fixes the rows on the server for every reader, so it refuses\n`{ "me": true }` \u2014 open on it with `opens`; on a record\'s related rows (`same`, `app`) and on a dashboard\'s List it\nnarrows by the reader and takes it, and those two take no comparison.\n\n## Changing an app\n\n1. `get_workspace` with `kind: "app"` names the app. `get_app` prints the `tree:`, the `node addresses:` (the address of\n each node the app draws, `(default)` on those the tree leaves to the platform) and the `version` line, whose id is the `base_version_id` to send. A member who is not the\n app\'s author is shown no tree. Keep the tree: no tool reads an earlier one.\n2. `set_app` with `app_id`, `base_version_id` (that version: a newer write is refused, not overwritten) and `nodes`,\n (a refused change writes nothing, so `dry_run` is for reading the permissions it would grant and remove). Every answer\n states the permissions the change grants and removes.\n3. `screenshot_app` shows the result.\n\n`nodes` maps an address to a patch of that node. Read against the customers List of the account review (\xA7 A complete\nworkspace, abridged), the call below makes `tier` read-only, moves the Orders Section ahead of Account, adds an Act to\nAccount and removes the Reading:\n\n```json\n{\n "type": "List",\n "entity": "customer",\n "children": [\n { "type": "Reading", "key": "ordered_by_tier", "by": "tier", "value": "total_ordered" },\n {\n "type": "Record",\n "children": [\n { "type": "Section", "key": "account", "title": "Account", "children": ["tier"] },\n { "type": "Section", "key": "orders", "title": "Orders", "children": [{ "type": "List", "entity": "order" }] }\n ]\n }\n ]\n}\n```\n\n```json\n{\n "app_id": "<id of the app>",\n "base_version_id": "<current_version_id>",\n "nodes": {\n "tier": { "edit": false },\n "orders": { "before": "account" },\n "mark_gold": { "type": "Act", "in": "account", "label": "Make gold", "when": { "tier": ["standard"] }, "set": { "tier": "gold" } },\n "ordered_by_tier": null\n }\n}\n```\n\nEach entry applies in order:\n\n- **An existing node:** a JSON merge patch (RFC 7396) of its props; `null` drops a prop, and a list (`children`,\n `columns`) replaces the node\'s whole.\n- **`null`:** removes the node and everything under it.\n- **An address naming no node, with `type` and `in`:** adds a node there; its key is the address\'s last word, and a\n Field states `field` in place of `type`.\n- **`in`, `before`, `after`:** move a node into another, or ahead of or behind a sibling.\n\nThe app\'s own props (`name`, `description`, `icon`, `color`, `words`, `scope`, `period`) change\nthrough `body`, a merge patch over the App node. `dry_run` checks a change and writes nothing.\n\n| To change | State |\n|---|---|\n| a field read-only | `"edit": false` on its Field, or on the Section, Side, Page or Record that draws it |\n| a column added or dropped | `columns` of the List, every column kept: a List that never stated `columns` draws none beyond the row\'s title and its `records` line |\n| a button | an Act node (\xA7 Act node); `confirm` makes its press ask first |\n| a label | the table\'s field `label` in `set_tables`, for every app; for this app alone, `body: { "words": { "<table>": { "fields": { "fee": "Consultation" }, "options": { "urgency": { "rush": "Same day" } } } } }`; a List\'s `label`, a Section\'s `title`, an Act\'s `label`, `add_label` on the add. A Field\'s `label` heads only a gallery or prose block |\n| the order of nodes and rows | `before` or `after` a sibling, or `in` another parent, in the node\'s entry (a Section ahead of another, a Field ahead of another in its Section); a List\'s `columns` read in the order stated; its rows read in its `sort`: `{ "sort": { "field": "collected_on", "desc": true } }` |\n| the row\'s second line | `row: { "subtitle": ["mechanic"] }` on the List: up to two fields under the title, in place of the table\'s `records` line, which stays as it is |\n| a picker\'s add | `add` on the link Field: `{ "add": ["species", "microchip"] }` asks those fields of the new row; `false` makes the picker only pick. It holds in every picker of that table in the app |\n| a linked row fixed or deleted where its link stands | `record` and `remove` on the link Field, or a row `title` that is a link: `{ "add": ["name", "kind"], "record": ["name", "kind"], "remove": true }` \u2014 the link\'s card opens the row in the drawer, saving those fields, with its Delete. Stated once per table, it holds on every card of that table in the app; a table a List of the app draws is worked in that List, and the statement there is noted and grants nothing |\n| a headline figure | a Reading in the List: `{ "type": "Reading", "in": "orders", "key": "prepaid_owed", "label": "Prepaid owed", "value": "prepaid", "where": { "stage": ["awaiting_payment"] } }` sums the rows in view; without a `value` it counts them |\n| a calendar and its date | `layout: "calendar"` on the List, and the date it places each row on is the first of `row.due`: `{ "layout": "calendar", "row": { "due": ["pickup_on"] } }`; absent, the table\'s `records.due` |\n| a record\'s child lines | a List inside the Record\'s Section over the child table: `{ "type": "List", "in": "details", "entity": "repair_line" }`; the child table (one link back to the record) and the record\'s rollup over its rows come from `set_tables` first. Lines priced off a book: `docs path="design/treatments/moves-and-numbers/priced-lines"` |\n\n**A node the tree leaves to the platform has an address too.** `get_app` lists the nodes the app draws, those the stored\ntree does not state marked `(default)`: the Record with its Sections and Fields, the status buttons, the band of readings.\nPatch one by its address and the call states it first: the nearest node the tree does state gains the default child that\nleads to it, as drawn (the whole Record under a List that states none, with the List\'s `row` picture and the `columns` the\ndefaults added; every Section of a Record that states no part; every status button of the app, each in the Section of its\nstage; every reading of the band, since stating one stands the others down), then the patch applies. Defaults\noutside that subtree stay defaults, and the answer\'s notes say what was stated. Removing every reading, or every status\nbutton, is refused: a set no node states is drawn again by the platform, and only `readings: false` or\n`row.status: { "edit": false }` on the List draws none. **A stated subtree stays as it is**: a\nfield or option the tables gain later is not added to it, so name it in the tree. `{ "due_date": { "edit": false } }`\nover a sparse tree makes that field read-only and takes away `saves <table>: due_date` alone. An address that ends several\ndrawn nodes is refused, as for stated ones.\n\n## What an app may write\n\nAn app states no list of what it writes: the tree derives it, and the generated workflows accept exactly that, so it is\nalso what an agent can do through the app\'s API. `get_app` prints it beside the tree.\n\n1. **A save of field `f` of table `E`** exists when a node draws `f` on a page of `E`\'s rows (a Field in a Record,\n Section, Side or Page, or a part of the head), the table lets a person write it (stored; not computed, stamped or the\n back side of a child\'s link), no Act of the app on `E`\'s rows writes it, and neither the node nor a container above it\n says `edit: false`. A row\'s stage is moved by an Act or the one button per move a status no Act moves gets, by hand only where `row.status` says `{ "edit": true }`, and by none where it says `{ "edit": false }`.\n2. **An add of `E`** exists for a List over `E` that writes its rows unless it says `create: false` (asking what it lists, and\n setting the link back to the record itself), and for an `add` on a Field drawing a picker of `E`. An absent `create`\n stands down where the table states no `singular` (nor the app\'s `words`), or where `where` keeps a select at an option an\n Act sets other than the one a new row starts at; a stated `create` list over such a table is refused. A roster\'s cells and\n a document are added only where `create: true`.\n3. **A Delete of `E`** exists for a List over `E` unless it says `remove: false`, and for the table of every Book of that\n List (a Book has no `remove` of its own). A roster\'s cells, a document and a desk\'s rows are deleted only where `remove: true`.\n `remove: false` takes this app\'s Delete away and no more: the record tools, the API and other apps still delete. A row no\n surface may delete is a table check with `on: ["delete"]`.\n4. **An act** exists for each Act node. Every field it sets, asks, files into, signs or records is the act\'s, and read-only\n everywhere else in the app.\n5. **The table\'s contract holds on every path** and is not restated here: required, unique, no overlap, field rules, table\n checks, stage owners, history and the read scope. (`natural_key` is read by an app\'s add and find-or-create alone.)\n\nDrawing is reading or saving; a List that writes its rows also adds and deletes them, until it says `create: false` or `remove: false`, and a List below a part or a Record that says `edit: false` writes, adds and deletes none.\n\n### What a table gives a component\n\n| Table fact | Feeds |\n|---|---|\n| The title | every row\'s name: Lists, a Record\'s head, link chips, pickers, an add |\n| The row\'s line (`records` subtitle, figure, image, mark, party, description) | the row\'s line and head, cards, a checklist\'s photo |\n| The stage (`records` status) | the stage chips, a List\'s `opens`, the badge, an Act\'s moves, stage owners gating acts, a Section\'s `at` |\n| Deadlines (`records` due, settled, frees) | countdowns and overdue marks, the default sort, attention order, spans on lanes, calendars and rosters |\n| Bounds (`records` limits, gates, tolerance) | the figure\'s meter, wherever it stands |\n| Tasks and cadence (`records` task, cadence) | the ring on task rows, a roster\'s owed days |\n| Applicability and starts (`records` applies, starts) | a field shown and asked only where it applies; an add\'s starting values |\n| A field\'s `required`, `default`, `default_from`, `natural_key`, `unique` | what an add asks, and what it starts at |\n| A field\'s `notation`, `format`, options, `options_where` | money, units, chips, and what a picker offers |\n| The table\'s `read_scope` | which rows each reader reads, in every List |\n\n### Where the rules stand\n\n\xA7 Rules of the app is every rule the check holds over an app, by id, whichever component it concerns. A\n**language** is decided by the app\'s own words: a tree names no locale, so what an app draws itself (its buttons, empty\nstates, refusals) is in the language its labels are written in, and `words` count as labels. Mix two languages in one\napp and it answers in two.\n\n## App node\n\nAn app draws its launcher tile, its words, its Lists and its API. It binds the tables it names: `set_app` fills\n`tables` with the id of every table alias the tree uses, and `templates` with that of every document template alias.\nA relabel on either side is a rename and no tree points at another table by accident.\n\n<!-- generated:start node-app -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The app\'s alias, its name among apps; the App node is addressed `/`, and no address in it starts with this key |\n| `name` | text | yes | The job, in the words of the people who do it |\n| `description` | text | no | What the job is for, in a sentence |\n| `icon` | text | no | A Lucide icon name in kebab-case the launcher tile draws |\n| `color` | [colour](#select) | no | The launcher tile\'s colour |\n| `words` | map of alias \u2192 [Table labels](#table-labels) | no | A table \u2192 the words this app draws for it |\n| `scope` | alias | no | A required one-row link of the first List\'s entity: the app works inside one row of the linked entity |\n| `reads` | `"shared"` | no | Every member reads every row, whatever the entity\'s read scope |\n| `uses` | list of alias (at least one) | no | Tables the app\'s Query and Workflow nodes read without drawing them, by alias: they join the tables the app reaches, so a workflow may name them |\n| `period` | `"month"` \\| `"quarter"` \\| `"year"` \\| [Dashboard period days](#dashboard-period-days) | no | The period a dashboard opens on |\n| `tables` | map of alias \u2192 text | no | The id of each table the tree names, by its alias: filled by set_app, never written |\n| `templates` | map of alias \u2192 text | no | The id of each document template the tree names, by its alias: filled by set_app, never written |\n| `children` | list of ([List](#list-node) \\| [Tab](#tab-node) \\| [Reading](#reading-node) \\| [Query](#query-node) \\| [Workflow](#workflow-node)) (at least one) | no | The first List at the root is the register, and a second is another app; a dashboard holds Tabs, Readings and Lists that open in another app. Queries and Workflows stand here too |\n\n#### Table labels\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `label` | text | no | The table\'s name in the plural (a register\'s heading, a block\'s, a link\'s picker) |\n| `singular` | text | no | One row of the table, as the add and empty sentences name it |\n| `fields` | map of alias \u2192 text | no | A field alias \u2192 the words this app draws wherever it names the field; the table\'s field keeps its own label |\n| `options` | map of alias \u2192 map of alias \u2192 text | no | A select field alias \u2192 option alias \u2192 its words |\n\n#### Dashboard period days\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `days` | `30` | yes | The last 30 days through today \u2014 the one span of days a dashboard opens on |\n\n<!-- generated:end node-app -->\n\n- **The first List is the register**: the working list of one table. Every further List at the root is a **log** beside\n it, at most 3, each stating its `label`: read as what was brought into or done to the register\'s rows (the files\n imported, the runs of a batch), opened as a register of its own inside the app, its reads, acts and workflows the\n app\'s. Only the organization\'s members are shown a log. A log is a table or cards with no `Tab`, `Book` or `compose`;\n its table is never the register\'s, a Book\'s or another log\'s; none beside `scope`. A list people work every day as\n a desk of their own is another app.\n- **A dashboard** is an App with no register: it holds `Tab`s, `Reading`s and read-only `List`s that open in\n another app (`app`). One `period` windows every reading placed in time: this month by default, or `quarter`, `year`\n or `{ "days": 30 }`. `words` and `scope` belong to a register.\n- **Query and Workflow** nodes stand at the root and are never drawn.\n- **Writes:** nothing itself.\n\n## List node\n\nA List draws rows of one table. What its rows are depends on where it stands:\n\n- at the App root: the register (the first) or a log (each further one);\n- under a Record, Section, Side or Page: the record\'s child rows, filed through the child\'s one link to the record\n (`via` where it has several);\n- under a roster List: the rows that fill the roster\'s cells;\n- with `same` or `app`, or on a dashboard: related rows, read only.\n\n<!-- generated:start node-list -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Absent, the entity alias |\n| `entity` | alias | yes | The table whose rows the List draws |\n| `via` | alias \\| text | no | The child\'s link to the record, where it has several. A List that only reads may state `<link>.<link>`: rows linked to the record only through a row of their own \u2014 their one-row link to that row, then that row\'s one-row link to the record |\n| `same` | alias | no | A one-row link of the record: lists the rows sharing that link\'s row, the record left out, read only |\n| `where` | [narrowing](#app-tree) | no | A fixed narrowing on the server, for every reader: a select holds one of these options, a yes/no this value, a number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), a field is `{ "empty": true \\| false }`, and `<link>.<field>` keeps the rows whose one-row link names a row holding a select or yes/no so (a path holds nothing on an add) \u2014 every entry ANDed. A List that only reads (`same`, `app`, a Record with `edit: false`) also bounds a date or number by a field of the record it stands beside, `{ "record": "<field>" }`, read when the rows are: a window of the record\'s own days (`{ "published_on": { "gte": { "record": "period_start" }, "lte": { "record": "period_end" } } }`); a bound the record holds blank keeps no row, and the same rows are handed to an Act\'s paper that names the List. A record bound narrows what is listed; it is never an access bound \u2014 the table\'s row rules are. An add holds a select\'s option and a yes/no\'s value; a bound or a blank holds nothing on an add |\n| `expect` | alias \\| [Expect](#expect) | no | One line per option of this select, or per row of this catalog link |\n| `under` | alias | no | A one-row link to the document that covers the line; lines stand grouped under their document |\n| `app` | text | no | The id of the register app its rows open in |\n| `row` | [List row](#list-row) | no | How this List reads its rows, where it differs from the table\'s `records` |\n| `label` | text | no | What the app\'s lists and the tab strip call the register; absent, the entity\'s label. Never the noun of its rows \u2014 the table\'s own label and singular (this app\'s `words`) are |\n| `columns` | list of (alias \\| [Edit column](#edit-column)) | no | Fields read after the row\'s title; `{ field, edit: true }` is the line\'s own control, at most 2. Absent: none, the row reads its title and its `records` line alone |\n| `filters` | list of (alias \\| [Tiered filter](#tiered-filter)) (at most 3) | no | Fields the reader narrows the rows by every day, at most 3; the stage chips always stand. A field is a select, member, link, date, yes/no or number range; `tiers` bands a number or date |\n| `search` | list of alias (at least one) | no | Fields the search box reads; absent, every word of the row |\n| `group` | alias | no | A field the rows stand grouped by; a gantt draws the groups as its lanes |\n| `sort` | [Sort key](#sort-key) \\| list of [Sort key](#sort-key) (at least one) | no | The order of the rows; absent, the first `records.due` soonest, else newest |\n| `opens` | [narrowing](#app-tree) | no | The view the register opens on, a chip the reader may take away |\n| `period` | `"today"` \\| `"week"` \\| `"month"` \\| `"quarter"` \\| `"year"` \\| [Period days](#period-days) | no | The period the List opens on, with Tabs; absent, this month |\n| `attention` | `false` | no | false: rows keep the list\'s own order; absent, those needing attention open first |\n| `readings` | `false` | no | false: the register leads with no band of figures; absent, rows that move, fall due or carry money lead with one. Never with Readings stated |\n| `layout` | [layout](#layouts) | no | How the rows are drawn: a word, or an object stating one layout with its data; absent, a table |\n| `compare` | [Compare](#compare) | no | Reads each row\'s before against its after: two files fields as a pair of photos, or two values of one type as one column |\n| `current` | alias | no | A child List of revisions: the child\'s date or number field that orders them. The row holding the latest value reads as current and the others fold under "superseded \xB7 N", one press to open; the record shows the current file through a lookup of the child ordered by that field |\n| `create` | boolean \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The fields an add asks. Absent: where this List writes its rows, the table\'s required fields, its natural key and its title; `false`: rows are not added here. Where the table states no `singular` (nor the app\'s `words`), or `where` keeps a select at an option an Act sets other than the one a new row starts at, an absent `create` adds nothing; the table\'s `singular` or a stated list adds. A roster\'s cells and a document add only where `true` |\n| `add_label` | text | no | The add button\'s words; absent, the platform\'s word and the entity\'s singular |\n| `remove` | boolean | no | Rows offer a Delete. Absent: they do where this List writes its rows; `false`: this app\'s Delete is gone, while the record tools, the API and other apps still delete \u2014 a row no surface may delete is a table check with `on: ["delete"]`. A roster\'s cells, a document and a desk\'s rows delete only where `true` |\n| `pick` | `true` | no | Also picks rows of the child that no record holds yet |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `children` | list of ([Tab](#tab-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Export](#export-node) \\| [Act](#act-node) \\| [Check](#check-node) \\| [Book](#book-node) \\| [Record](#record-node) \\| [List](#list-node)) (at least one) | no | What the List holds: its Tabs, Readings, Progress, Exports, Acts and Checks, its Books, the Record a row opens, under a roster the List of its cells, and in a child List with `under` the List of the documents the lines stand under (the one place a document is added or deleted) |\n\n#### List row\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `title` | [List row title](#list-row-title) | no | The row\'s title field, stated for its props alone: `edit: false` reads it, `add` is its picker\'s add, `false` its picker only picks; where it is a link, `record` and `remove` how its card works the row it names |\n| `subtitle` | list of (alias \\| [Field](#field-node)) (1\u20132) | no | Up to two fields read under the title, in place of the table\'s |\n| `figure` | alias \\| `false` | no | The one number a row stands for, in place of the table\'s; false, none |\n| `image` | alias \\| `false` | no | A files field that pictures a row, in place of the table\'s; false, none |\n| `due` | list of alias (at least one) \\| `false` | no | Dates that count down, in place of the table\'s; false, none |\n| `status` | `false` \\| `{ "edit": true \\| false }` | no | false: the stage is not shown. `{ edit: true }`: the stage is also moved by hand. `{ edit: false }`: nobody moves it in this app, another app does. Absent, the stage\'s moves are buttons |\n\n#### List row title\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `edit` | `false` | no | false: the title is read; absent, it is written where the table lets a person write it |\n| `add` | `false` \\| `true` \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The new row this field\'s picker offers asks these fields; `true`: the target\'s required fields, its natural key and its title. Stated once per target table, and it holds in every picker of that table in the app. `false`: this link only picks, in every picker of it the app draws, not only where it is written |\n| `record` | list of alias (at least one) | no | The fields of the row this link names that this app saves from the link\'s card, which opens the row in the drawer: stored fields of the linked table a person writes. Stated once per linked table, and it holds on every card of that table in the app, where no List of the app draws its rows. Absent, the card reads the row |\n| `remove` | `true` | no | true: the row this link names is deleted from the link\'s card. Stated once per linked table, with its `record`, and it holds on every card of that table in the app. Absent, the card deletes nothing |\n\n#### Edit column\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The column\'s field |\n| `edit` | `true` | yes | true: the column is the line\'s own control, written in place |\n\n#### Expect\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The child\'s title when the record holds one row per value of it: a single select (one line per option) or a one-row link to a catalog (one line per row) |\n| `of` | alias | no | With a select: the record\'s own multi-select holding the options this record expects a line for, in place of every option |\n| `only` | list of alias (at least one) | no | With a select: the options this List holds a line for; never with `of` |\n\n#### Compare\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `before` | alias | yes | A field of the List\'s rows holding what stood before: a files field (the photo of the place before the work), or a value \u2014 text, a number, a date or a single select (the price a row held before a sheet changed it) |\n| `after` | alias | yes | Another field of the same type holding what stands after: the photo of the same place once the work is done, or the value the row holds now |\n| `edit` | `true` | no | The after is written on the line |\n\n#### Tiered filter\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A number \u2014 stored, or a formula or rollup reading one \u2014 in one unit for every row |\n| `tiers` | list of number (at least one) | yes | The breakpoints between its bands, ascending, in the field\'s stored unit: under the first, from each to the next, from the last up \u2014 each band worded in the field\'s notation; a filter picks several at once |\n\n#### Sort key\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The field rows are ordered by |\n| `desc` | `true` | no | Latest or largest first; absent, soonest or smallest first |\n\n<!-- generated:end node-list -->\n\nA List takes only the props its place lists:\n\n<!-- generated:start list-places -->\n\n| A List standing in | Takes |\n|---|---|\n| the register (the first List of the App) and a log (each further one) | `key`, `entity`, `row`, `label`, `columns`, `filters`, `search`, `group`, `sort`, `opens`, `period`, `attention`, `layout`, `create`, `add_label`, `remove`, `readings`, `where` |\n| a record\'s child rows, as a table, a checklist or a desk | `key`, `entity`, `via`, `label`, `columns`, `create`, `add_label`, `where`, `under`, `filters`, `search`, `group`, `sort`, `remove`, `pick`, `layout`, `compare`, `current`, `when`, `expect`, `row` |\n| a record\'s child rows with `layout` an agenda | `key`, `entity`, `via`, `label`, `columns`, `create`, `add_label`, `remove`, `layout`, `when`, `row` |\n| a record\'s child rows with `layout` `"timeline"` | `key`, `entity`, `via`, `label`, `create`, `add_label`, `remove`, `layout`, `when`, `row` |\n| a record\'s related rows, read only (`same`, `app`, or a Record that is `edit: false`) | `key`, `entity`, `via`, `label`, `where`, `columns`, `app`, `same`, `when`, `row` |\n| the documents a child List\'s lines stand under (`under`), a List inside it | `key`, `entity`, `create`, `remove` |\n| the cells of a roster, a List inside it | `key`, `entity`, `expect`, `create`, `remove`, `row` |\n| a dashboard\'s list, read only | `key`, `entity`, `label`, `where`, `columns`, `app` |\n\n<!-- generated:end list-places -->\n\n- **Children:** Tab, Reading, Progress, Export, Act, Check, Book, Record, and a List for a roster\'s cells. A Check or an\n Act stands in the List whose rows it works on: on each row (its \u22EF and the open row\'s), or with `on` on the ticked rows\n or every row in view.\n- **A child List\'s Record** holds `edit`, Fields and Lists only: its Fields are what the rows\' saves write, and a row\n opens in place under itself \u2014 its fields, then each List of its own children (an entity linking to the List\'s), stated\n as a child List one level up with its Acts, Checks and Record, at any depth. With `edit: false` the List only reads, as\n a related-rows List does: it states its `label`, takes none of `filters`, `search`, `group`, `sort`, `create`,\n `remove` or `layout`, and may reach rows linked to the record only through a row of their own (`via:\n "<link>.<link>"`).\n- **Documents:** in a child List with `under`, a List over the document\'s table (`key`, `entity`, `create`, `remove`,\n Acts, Checks and a Record of the document\'s Fields and Lists) is the one place the documents are added or deleted;\n `create` states `true`, and `remove` needs `create: true` beside it.\n- **Writes:** a List that writes its rows adds and deletes them \u2014 `create: false` and `remove: false` turn each off \u2014 `pick` links existing rows, an `edit: true` column writes that\n field on the line, and a layout move (a gantt bar, a lane\'s block, a plan\'s item) writes only fields the app already\n writes on the row\'s page.\n- **`create` asks:** `true` asks the title, the natural key and every required field a person writes \u2014 never a computed\n or stamped field, nor the link the place already sets. An `expect` list also asks the key it makes its lines by, and\n their picture. A list of fields overrides it; `false`, rows are not added here.\n- **`row`** overrides what the table\'s `records` say of the register\'s rows in this List alone (a cashier\'s figure is what\n is still owed): the subtitle, figure, image and deadlines stated replace the table\'s. A child List, a related List and\n a roster\'s cells read their rows as their table does, so their `row` states only a picker\'s add and a link\'s card (on its title or\n subtitle) or the stage moved by hand (`row.status`).\n- **`filters`** are the fields a reader narrows by every day, none to three \u2014 a quota never; each earns its place by\n daily use. A date filters by a range of days, a number by a range in its unit, `{ "field", "tiers" }` by the bands its\n breakpoints cut. A threshold the business names ("large orders") is a formula yes/no, never a range. The stage chips\n always stand.\n- **`opens`** is the view the register opens on, each entry a chip the reader may take away; absent, the open work\n (rows not at a `closed` stage).\n- **`expect`** makes one line per option of a select, or per row of a catalog link, and an empty line adds its row\n (a checklist of papers per case). **`under`** groups lines under the document that covers them.\n- **`compare`** reads each row\'s before against its after: two files fields as a pair of photos, or two values of one\n type as one column under the after\'s label.\n\n## Layouts\n\nA List\'s `layout` is how it draws its rows: a word (`"table"`, `"cards"`, `"calendar"`, `"roster"`, `"timeline"`) or an\nobject stating one layout and the data it reads (`{ "gantt": \u2026 }`, `{ "lanes": \u2026 }`, `{ "plan": \u2026 }`, `{ "agenda": \u2026 }`,\n`{ "checklist": \u2026 }`, `{ "desk": \u2026 }`). Absent, a table: the word `"table"` is the register\'s, and a child List states a\nchecklist, a desk, an agenda or `"timeline"`, else leaves `layout` out. Each layout reads what the rows already state \u2014 their\n`records` line, stage and dates \u2014 and the keys a layout adds stand on its section: here for an agenda and a checklist, \xA7 Time and place layouts for the rest, \xA7 Publish desk for a desk. Which one a job needs: the\n`design` reference. The register lays out as a table, cards, a calendar, a roster, a gantt, lanes or a plan; a record\'s\nchild rows as a table, a checklist, a desk, an agenda or a timeline.\n\n### Table\n\nAbsent `layout`, each row is one line: the table\'s `records` entry \u2014 its picture or mark, title and the line under it \u2014\nthen the stage, the `columns`, and the figure at the right; the title, the stage and a column of days or amounts sort\nthe rows by their header. A table groups its rows by `group`; a table or cards reads `Tab`s and `Book`s.\n\n### Cards\n\n`"cards"` draws each row as a card led by its picture \u2014 the `records` `image`, or the List\'s `row.image`: its title and\nthe line under it, its stage and what needs the reader (a deadline, a warning), then its figure and the `columns` at its\nfoot. Cards are for rows found by their picture (a product, a vehicle, a damage photo); rows with no picture read better\nas a table.\n\n### Calendar\n\n`"calendar"` places each row on its day \u2014 a month, a week, a day or a list (a phone reads the list or a day): at its\nhour where the date holds one, over its span where the row\'s line holds a second date after it, its line\'s other words\nand the `columns` after its title. The calendar reads the window in view, and its count and the stage chips count that\nwindow, opening on every row.\n\n### Roster\n\n`"roster"` draws the List\'s rows down the side and the days of a week or a month across; the roster\'s cells are a List\ninside it over the table whose rows fill the days \u2014 each stands on one row by its single link to it, and on the first\ndate of its line (a shift on a person\'s day, a run on a vehicle\'s), on every day through the second date where its line\nholds one. A cell reads its row\'s stage, else the first single select on its line (a shift\'s kind), several rows their\ncount. A day holding none stays empty: nothing was due, which is never an absence. A filled cell opens its row; an empty\none adds a row there, the register row and the day set. With `expect` on the cells (a single select of those rows) the\ncolumns are its options in place of days \u2014 a checklist of papers \u2014 and `of` names the ones each row expects: a gap among\nthem reads as one and adds its row, the others stand blank, and each row reads what it holds of them, `2/3`. Over days, a\nroster reads the `cadence` its table\'s `records` states (a site\'s diary page, Monday to Saturday from breaking ground to\nhandover): a past day owed with none is marked missing and counted, and each row reads the days it holds of those owed. A\nroster states no `columns` and needs no readings: its legend counts each state in view.\n\n```json\n{ "type": "List", "entity": "operator", "layout": "roster", "filters": ["depot"], "children": [{ "type": "List", "entity": "run" }] }\n```\n\n### Timeline\n\n`"timeline"` reads a record\'s child rows as a log of dated entries, newest first, with a composer where the app adds\nentries, each corrected and removed where it stands. It is never the record\'s status history: the rail shows each\nmove. A timeline\'s moment is its child\'s first date, and who its member field. Rows each what happened on a day and who \u2014\na date on their line, a member, no stage and no figure \u2014 read best as a timeline.\n\n### Agenda\n\n`{ "agenda": { \u2026 } }` reads a record\'s child rows as planned entries on a time axis: by day, soonest first, each with its\npicture and the day that is today marked. The day is the first date on the child\'s row line (its `records` title or\nsubtitle); within a day entries follow a datetime there, else the first single select after the date. An entry with no day\nyet is a plan, listed first and dated in place. `start` is the record\'s date that is the first day: each day\'s heading\ncounts from it (Day 1, Day 2).\n\n<!-- generated:start layout-agenda -->\n\n#### Agenda keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | no | The record\'s date that is the first day: each day\'s heading counts from it; absent, the weekday and the date alone |\n\n<!-- generated:end layout-agenda -->\n\n### Checklist\n\n`{ "checklist": { \u2026 } }` reads a record\'s child rows, which are tasks, as a checklist of checks: each one\'s mark \u2014 passed\nat a `closed` option, failed at a `failed` one, else still to check \u2014 its title, its columns as its words (a text column\nits note, on its own line), and on a line under its title its photo and its clause. Never grouped nor written in place.\n`passed` alone sets the mark, so rows with no `task` and no status are checks: a measured yes/no is the verdict. With\n`expect` on the standard\'s items, each item is a line marked the same way (a missing one still to check), filled in\nplace by the block\'s `columns` (a measured value, a note, a photo), so `photo` and `clause` are not named.\n\n<!-- generated:start layout-checklist -->\n\n#### Checklist keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `photo` | alias | no | The child\'s files field holding the photo each row is checked by; absent, the child\'s `records.image` |\n| `clause` | alias | no | The child\'s one-row link to what each row is checked against \u2014 a standard\'s clause, a rule |\n| `failed` | list of alias (at least one) | no | Options of the child\'s status a row stands failed at \u2014 none of them closed |\n| `passed` | alias | no | The child\'s yes/no field (stored or a formula) setting each row\'s mark: yes passed, no failed, empty still to check |\n\n<!-- generated:end layout-checklist -->\n\n## Time and place layouts\n\nThe layouts that place a List\'s rows in time or on a plan. Each is a `layout` object stating the data it reads.\n\n### Lanes\n\n`{ "lanes": { "by": \u2026 } }` draws each row of the linked table as a lane (a chair, a machine, a room), empty ones too,\nand each row of the List as a block from the first date on its line to the second: over the hours of one day where the\nfirst holds its hour, else over the days of one day, a week or a month. A block reads the row\'s name and the first of its\n`columns`; a free stretch between blocks adds a row there, its lane and its start set, and its end where the next block\nbounds it. Rows naming no lane stand first, in a lane of their own; where the app writes the link, each block moves to\nanother lane from its menu. Inside a `scope`, the lanes are the ones whose own one-link names that scope\'s row. A span\'s\nend holds its day unless `records` names it the day the row `frees`.\n\n`loads` names what a lane carries: `{ "weight": "payload", "volume": "box" }` sums each block\'s number and reads it\nagainst the lane row\'s own, at most two. A lane holds its blocks while they stand, so it reads the most they add up to at\none time in the window, named, past its bound, by how much and on which day or hour. The two are in one unit, or two of\none dimension. A bound a single row must keep (no parcel heavier than its van) is a `max` on the field through the link,\nwhich refuses the move.\n\n<!-- generated:start layout-lanes -->\n\n#### Lanes keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `by` | alias | yes | A one-link of this entity whose target\'s rows are the lanes (a chair, a machine, a room), each drawn even when empty |\n| `loads` | map of alias \u2192 alias | no | What a lane carries against what it holds, at most 2: a number of this entity each block adds \u2192 a number of the lane\'s row it is held within, in one unit or two of one dimension |\n\n<!-- generated:end layout-lanes -->\n\n### Gantt\n\n`{ "gantt": { \u2026 } }` draws each row as one bar on one axis of time: `{ "start": "etd", "end": "eta", "milestones":\n["cut_off"], "planned": { "end": "booked_eta" }, "progress": "done", "after": "waits_on" }`. `end` defaults to the table\'s\nfirst `due`, and a table with neither is refused; `start` and `end` are two dates of the row, both days or both moments,\nand each planned date is read as `start` is. `progress` is a percent (0\u2013100); absent, a row whose record lists its tasks\nfills to how many are done. `after` is a link to rows of the same table, each ending before the row starts. The bars\nstand in the lanes of the List\'s `group`. Where the app writes the row and a person writes a bar\'s date, the bar is\ndragged through the row\'s save; a computed date, one an Act sets, or a row the app does not write drags nothing.\n\n<!-- generated:start layout-gantt -->\n\n#### Gantt bar\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | yes | A date of this entity each row\'s bar starts at (a sailing, a hire\'s start, a task\'s start) |\n| `end` | alias | no | The date each row\'s bar ends at, read as `start` is \u2014 two days or two moments; absent, the entity\'s first `due` |\n| `milestones` | list of alias (at least one) | no | Dates of the row marked on its bar\'s line as diamonds, each named by its label (a cut-off, a delivery, the end of free time) \u2014 never `start` or `end` |\n| `planned` | [Gantt plan](#gantt-plan) | no | The plan each row is read against, drawn as a thin bar under its own \u2014 each date read as `start` is; a missing one is the bar\'s own |\n| `progress` | alias | no | A percent of this entity (0\u2013100) filling each row\'s bar as far as the work is done; absent, how many of the row\'s tasks are done fills it where its record lists them, else the bar wears its status\'s tone |\n| `after` | alias | no | A link of this entity to its own rows each row waits on \u2014 it starts once they end (finish-to-start), an arrow from each; a row starting before one ends is drawn in the danger tone |\n\n#### Gantt plan\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | no | The date the row was planned to start at |\n| `end` | alias | no | The date the row was planned to end at (a booked arrival) |\n\n<!-- generated:end layout-gantt -->\n\n### Plan\n\n`{ "plan": { \u2026 } }` draws things that stand at a place \u2014 pallets in a warehouse \u2014 on a plan of the places. The places are\nthe rows of a table whose `records` states a `plan_place` (a bay, an open area or a landmark, each at its spot on a sheet\nof the plan); `at` names the one-link of the List\'s rows to a place and, for an item in a bay, the row (`across`) and the\ntier (`up`) it stands in, counted from 1. A bay is drawn as its rows and tiers, an area with a count, a landmark as a\nlabelled frame; the reader sees a plan, one bay enlarged, and the same plan in 3D. The List may read other tables as\n`Book`s: one plan of every table\'s items. Each item carries `__reach` \u2014 how many items stand in the way of taking it out\n(those above it where the place is `stacked`, and those in the rows between it and the side `reach_from` says the bay is\ntaken from) \u2014 so the list, its export and an agent\'s read all carry it; `<table>_plan_load` counts the rows each place\nholds. A move through the app is refused unless it clears the place, row and tier all three, or places the item at\na free cell of a bay \u2014 resting on a present item where the place is `stacked` \u2014 or at an area. Every other write (a grid edit, an import, a tool) holds one item to a cell only where the item\'s table states `unique: [<at>, <across>, <up>]`; a plan over a table stating none is noted. A plan opens on every row\nits `where` keeps, with no stage chip narrowing it. Rows with no place wait in a tray beside the plan. An Act that asks exactly a place (a link to the plan\'s places) and two numbers (the row and the tier) is answered by a pick on the plan, never by a form. On a desk a press opens a row\'s record in the panel beside the plan.\n\n```jsonc\n// table "bay"\n"title": "code",\n"records": { "plan_place": { "kind": "kind", "x": "x", "y": "y", "across": "rows", "up": "tiers", "reach_from": "taken_from", "stacked": "is_stack" } }\n```\n\n```json\n{ "type": "List", "entity": "pallet", "filters": ["line"], "layout": { "plan": { "at": "bay", "across": "row", "up": "tier" } } }\n```\n\n```jsonc\n// table "pallet": one item to a cell on every write, not only through the app\n"unique": [["bay", "row", "tier"]]\n```\n\n<!-- generated:start layout-plan -->\n\n#### Plan keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `at` | alias | yes | A one-link of this entity to an entity whose `records` states a `plan_place`: where each row stands |\n| `across` | alias | yes | A number field: the row of a bay a row stands in, counted from 1; empty at an area |\n| `up` | alias | yes | A number field: the tier of a bay a row stands at, counted from 1 at the ground; empty at an area |\n| `fill` | alias | no | A single select the items are coloured by; absent, the first single select among the register\'s filters |\n| `mark` | [Plan mark](#plan-mark) | no | A row holding the option says the option\'s label on its box and is hatched in the option\'s own colour, and the legend names it by the label; in books, the select is matched by option alias, and a book lacking the select never marks its rows |\n| `size` | [Plan size](#plan-size) | no | Each row\'s box as it stands in the world, in the one length unit its places\' `size` states: the option the row holds of the select names its box\'s width across, depth along and height; in books, matched by option alias, as `mark` is; a row holding an option it does not name is drawn at its place\'s cell |\n\n#### Plan mark\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A single select of this entity |\n| `option` | alias | yes | An option alias of that select |\n\n#### Plan size\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A single select of this entity |\n| `options` | map of alias \u2192 [Plan size box](#plan-size-box) | yes | The box of each option alias of that select |\n\n#### Plan size box\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `across` | number | yes | The box\'s width across the bay\'s rows |\n| `along` | number | yes | The box\'s depth along them |\n| `up` | number | yes | The box\'s height |\n\n#### Plan place\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `kind` | alias | yes | A single select, options aliased exactly `bay`, `area`, `landmark`: a bay holds rows and tiers, an area neither, a landmark nothing |\n| `sheet` | alias | no | The plan sheet it is on: a single select, or a one-row link to an entity whose `records` states its `image` \u2014 the sheet\'s drawing, which the places are drawn over (with `scale`) |\n| `scale` | alias | no | A number field of the sheet entity `sheet` links to: how many cells wide its drawing is, the plan\'s scale. Required where that entity states an `image`, and only then |\n| `x` | alias | yes | A number field: left edge, in cells |\n| `y` | alias | yes | A number field: top edge, in cells |\n| `width` | alias | no | A number field: an area\'s frame width, in cells |\n| `depth` | alias | no | A number field: an area\'s frame depth, in cells |\n| `across` | alias | yes | A number field: a bay\'s rows |\n| `up` | alias | yes | A number field: a bay\'s most tiers |\n| `reach_from` | alias | no | A single select, options aliased exactly `first`, `last`, `both`: the side boxes leave a bay from; a bay that names none has no reach and no arrow |\n| `stacked` | alias | no | A boolean field: true where items at this place rest on the one below (a stack), so a tier above the first needs a present item under it, shows floating without one, and an item\'s reach counts the items above it; false or empty, items sit at any tier and only the rows between a bay\'s reach side and the item are in the way |\n| `size` | [Plan place size](#plan-place-size) | no | The real size of ONE cell of the place, for 3D: three number fields stating one length `unit` alike. Absent, a cell is drawn at the size most of the sheet\'s places state, a unit cube where none states one |\n\n#### Plan place size\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `across` | alias | yes | A number field: the width of one cell across the rows |\n| `along` | alias | yes | A number field: the depth of one cell along the place |\n| `up` | alias | yes | A number field: the height of one cell, one level |\n\n<!-- generated:end layout-plan -->\n\n## Publish desk\n\nRows that are each a place the record is sent \u2014 a notice\'s boards, a listing\'s portals \u2014 are a record\'s child List with\n`expect` on the child\'s one-row link to the catalog of those places and `layout: { "desk": \u2026 }`:\n\n```json\n{\n "type": "List",\n "entity": "posting",\n "expect": "board",\n "columns": ["posted_link", "refusal"],\n "layout": { "desk": { "bodies": ["wording", "wording_abroad"], "text": "goes_out_as", "media": "pictures", "link": "page", "group": "reach", "network": "kind", "networks": { "company_page": "linkedin" } } }\n}\n```\n\nThe desk is where the post is written. `bodies` are the record\'s plain text fields it is written in (one per language,\nsay), each its editor between the list and the preview, saved as any field of the record is and drawn nowhere else on it.\nThe preview is `text` read over what is on screen: where `text` is a formula of the child, the desk computes that same\nformula over what is typed, so the post moves as it is written, and a place not ticked previews the words its row would\nhold. For that the formula reads only what a screen holds whole: stored values of its row, the record through lookups over\nthe link back to it, the place through lookups over `expect`, and formulas of the row over those \u2014 a rollup, a lookup\nthrough another link or the clock is refused, naming the field.\n\nEvery place the link admits is a line with a tick and that place\'s face: its picture, else the mark of the network it is on\nwhere `networks` names one. `group`, another single select of the place, stands the lines under its options in their\norder: a kind holding a ticked line is open, one holding none folded to its heading and its count. A tick makes the line\'s\nrow from its key alone \u2014 so nothing else the child requires is left without a default \u2014 and an untick deletes it until the\nrow\'s stage is `closed`: what went out is not taken back. It asks first only where a person wrote on the row. One line is\nworked beside the list and read as the post that place\'s feed shows: `text` is the child\'s words for that place, `media`\nand `link` the record\'s, and `network` the place\'s single select whose options `networks` names to a feed; an option not\nnamed reads on a plain card. Under the bodies stand the List\'s `columns` a person writes of the line\'s row; under the post,\nthe press copying its words beside the Acts of the child, then what the line and the `columns` hold that nobody writes\nthere (the link it went out at, why it was refused). A value an Act or a workflow fills is stated as that Act\'s, so the\ndesk never offers it as an empty box. Sending is an ordinary Act of the record.\n\n<!-- generated:start layout-desk -->\n\n#### Desk keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `bodies` | list of alias (1\u20133) | no | The record\'s plain text fields the post is written in, in order \u2014 one per language, say \u2014 at most 3: each its editor on the desk, between the list and the preview, and drawn nowhere else on the record |\n| `text` | alias | yes | A text of the block\'s rows \u2014 stored, or a formula yielding text \u2014 holding the words that go out on that row\'s destination. A formula is computed on the desk too, over what is typed: it reads stored values of its row, the record through lookups over the link back to it, the destination through lookups over `expect`, and formulas of the row over those \u2014 never a rollup, another link or the clock |\n| `media` | alias | yes | A files field of the record: the pictures or the video the post carries |\n| `link` | alias | no | A text field of the record holding a link (`format: "link"`), carried as the post\'s card |\n| `network` | alias | yes | A single select of the entity `expect` links to, saying which network each destination is on |\n| `group` | alias | no | Another single select of the entity `expect` links to \u2014 the destination\'s kind \u2014 the lines stand grouped by: each option a heading in the select\'s order, a group holding no ticked line folded to its heading and its count, destinations holding no option last |\n| `networks` | map of alias \u2192 `"facebook"` \\| `"instagram"` \\| `"threads"` \\| `"x"` \\| `"linkedin"` | yes | That select\'s options, each by its alias, to the network whose feed the post is previewed in \u2014 facebook, instagram, threads, x, linkedin. An option not named has no feed to imitate: its words read as a plain card |\n\n<!-- generated:end layout-desk -->\n\n## Record node\n\nA Record draws what a row opens: the head, then the parts.\n\n- **The head:** title, subtitle, picture, mark, party, stage badge, figure and meter, deadlines, description \u2014 always the\n table\'s `records` facts, overridden by the List\'s `row`. A field the head draws is never also a Field of the record.\n- **No Record node:** under the register, the platform designs the record\'s Sections from the rows: the fields the\n head does not draw, child Lists, papers and Acts. A stated Record stands exactly as written, so it replaces them.\n Under a child List, an absent Record is every field of the child except the link back.\n- **Children:** either parts \u2014 `Section`, `Side`, `Page` \u2014 or, for a one-part record, `Field`, `List`, `Reading`,\n `Progress`, `Statement` and `Act` directly. The two are never mixed. Below the register \u2014 a child List\'s, a document\'s\n or a roster cell\'s \u2014 a Record holds `edit`, the Fields its saves write and the Lists of its rows\' own children; a\n Book\'s holds Fields and `edit` only.\n\n<!-- generated:start node-record -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `door` | `"page"` \\| `"drawer"` \\| `"beside"` | no | Absent, a page where a part is a step, a Side or a Page or there are more than 3 parts, else a drawer |\n| `comments` | `true` | no | The record carries comments; absent, nobody comments |\n| `edit` | `false` | no | false: nothing the record draws is written |\n| `compose` | [piece](#composed-page) | no | The page stated as a tree of finished pieces; a Record with `compose` holds no children |\n| `children` | list of (alias \\| [Field](#field-node) \\| [Section](#section-node) \\| [Side](#side-node) \\| [Page](#page-node) \\| [Outside](#outside-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | Parts (Section, Side, Page), or for a one-part record its Fields, Lists, Readings, Progress, Statements and Acts directly \u2014 never both; Outside parts stand beside either. Absent, the platform designs the Sections from the rows: the fields the head does not draw, child Lists, papers and Acts. Stated, it stands exactly as written |\n\n<!-- generated:end node-record -->\n\nThe door is a page where a part is a step, a Side or a Page, or where there are more than 3 parts; else a drawer. `door`\nis a judged choice of how a job is worked (one after another over a long record), so it is kept. From three parts on, the\nrecord is navigated by their titles: a page by a rail that jumps to each (a strip of the same names on a phone), a drawer\nby the same strip pinned over its scroll; with fewer, the parts just stack.\n\n`comments: true` lets people comment, and is opt-in: state it only where the job\'s people discuss a record. A door in the\nrecord\'s head then opens the comments in a panel beside it, with one composer. A record shows no activity and no audit\nlog: a history an app shows is table-backed \u2014 `records.status.dates`, `records.status.history`, an event table an act\nmakes \u2014 stated where the app shows it.\n\nA field, a Reading and an Act is each placed once on a record: the head, or one part. A Section or a List with `when` is\ndrawn only while each named select of the record holds one of its options.\n\nA page the parts cannot say is a **composed page** (\xA7 Composed page): `compose` states the record as a tree of finished\npieces, and the Record holds no children.\n\n## Section node\n\nA titled part of a record, and optionally a step of its path. A Section draws its Fields, Lists, Readings, Progress,\nStatements and Acts in reading order; its Acts stand at its foot.\n\n<!-- generated:start node-section -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique among its siblings; part of the node\'s address. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The part\'s heading |\n| `description` | text | no | A line under the heading |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `edit` | `false` | no | false: nothing in this part is written |\n| `at` | list of alias (at least one) | no | Options of the record\'s status during which this part is the work now: a step of its path |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | What the part draws, in reading order; its Acts stand at its foot |\n\n<!-- generated:end node-section -->\n\n- **`at`** names the stage options during which the section is the work now \u2014 only on a record that\n truly moves through those stages: the rail marks it done, now or waiting, and the section stands even holding nothing\n yet. It never hides a field, locks it or gates an act.\n- **Acts:** every Act on a record stands in the Section whose work it is. An Act moving the stage and offered at ONE step\n stands at that step\'s foot when it moves the work forward, to an exit later in the stage\'s options, or back to a step the\n work passes anyway; one offered at several steps, or moving back into a detour, stands in the Section it enters. A\n correction (`danger`, and not one of its step\'s outcomes) waits in the Section heading\'s \u22EF.\n- **A detour** \u2014 a stage the work only steps aside into from one step and leaves only back to it (a pause, a repair) \u2014\n stands before that step, its option and its Section alike.\n- **An exit** stands only once the record is in it, its reason there and its rail item apart from the path; until then the\n Act waits in the header\'s \u22EF.\n- **Writes:** its Fields and its Acts.\n\n## Side node\n\nThe one part drawn beside the rest \u2014 the photos looked at, the pool picked from. At most one Side to a record, on a page\ndoor, and never also a Page.\n\n<!-- generated:start node-side -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique among its siblings; part of the node\'s address. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The part\'s heading |\n| `description` | text | no | A line under the heading |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `edit` | `false` | no | false: nothing in this part is written |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | What the part draws, in reading order; its Acts stand at its foot |\n\n<!-- generated:end node-side -->\n\nIt holds what a Section holds. Writes: its Fields and its Acts.\n\n## Page node\n\nA part on its own page, off the record\'s scroll, with a way back \u2014 rarely, for a long related list or a workspace of its\nown that would make the record worse stacked. Never on a drawer, and never a step: its title names an address no other\npage shares (a letter or a digit at least), and not every part is a Page.\n\n<!-- generated:start node-page -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique among its siblings; part of the node\'s address. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The part\'s heading |\n| `description` | text | no | A line under the heading |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `edit` | `false` | no | false: nothing in this part is written |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | What the part draws, in reading order; its Acts stand at its foot |\n\n<!-- generated:end node-page -->\n\nIt holds what a Section holds. Writes: its Fields and its Acts.\n\n## Outside node\n\nWhat one record shows, and which acts it allows, to a person with no seat who opens a link a member made of it. A member\npresses Create link on a record while its status stands in `at`; the link opens that one record until it expires, is\nrevoked, or its act moves the status out of `at`. The person sees the stated Fields and Lists and presses the stated Acts\nand nothing else. It stands directly in a Record of a table with a stored status select: the register\'s, or the Record of\na List below the register, whose rows the link then opens.\n\n<!-- generated:start node-outside -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique in the app: a link to the record names it. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The heading of the page the person without a seat opens |\n| `description` | text | no | A line under the heading |\n| `at` | list of alias (at least one) | yes | Options of the record\'s status during which a link to it opens; none is owned by a stage, and each Act moves the status out of them |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Act](#act-node)) (at least one) | yes | Exactly what the link shows and allows: Fields read as values, Lists of child rows read as they are, and the Acts the person may press, each moving the status out of `at` |\n\n<!-- generated:end node-outside -->\n\n- **Fields** are read as values, in order; a lookup, a rollup or a formula is computed for the link only if stated here.\n A Field states nothing but its alias.\n- **Lists** read the child rows under the record as they are: `columns`, `label` and `where`.\n- **Acts** are the link\'s alone: no member, visitor or agent presses them. Each states `key`, `label`, `confirm`, `when`,\n `requires`, `asks`, `set`, `template` with `into`, or `sign`. `when` absent is the status held in `at`; `set` moves the\n status to an option outside `at`, so each press ends the visit. `asks` are text, numbers, dates or listed options. A\n `sign` is by a text field the signer types, with no witness: the link is the attestation.\n- `at` names options no stage owns, and no two Outsides of a table open during the same option.\n- **Writes:** its Acts, and nothing else.\n\n## Tab node\n\nA Tab is one view of a List\'s rows over a period, or one sheet of a dashboard.\n\n- **In a List:** a register reporting a period \u2014 what stands at its end beside what came and went during it (the stock,\n the hires out, the tickets open) \u2014 reads its rows as Tabs over one `period` the reader picks above them. A Tab holds\n the rows `dated` within the period, those `open` at its end, or lists every row and draws what a child\'s rows came to\n in the period (`totals`). A Tab narrows its rows and holds no nodes; Readings stand beside it in the List and read the\n open Tab, or the one their `tab` names. Never date chips set by hand, and never a Tab per stage, which the chips split.\n- **On a dashboard:** a Tab holds its Readings and Lists as children. A dashboard with Tabs (2 to 5) draws a strip under\n its title that picks one, the first open.\n\n<!-- generated:start node-tab -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | Unique among its siblings; part of the node\'s address |\n| `label` | text | yes | What the tab holds, in the reader\'s words |\n| `dated` | alias | no | A date: the tab holds the rows dated within the List\'s period |\n| `open` | [Tab open](#tab-open) | no | The tab holds the rows open at the period\'s end: begun, and not yet ended |\n| `totals` | [Tab totals](#tab-totals) | no | Each row\'s figures over a child\'s rows in the period |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `sort` | [Sort key](#sort-key) \\| list of [Sort key](#sort-key) (at least one) | no | The order this tab\'s rows read in; absent, the List\'s |\n| `children` | list of ([Reading](#reading-node) \\| [List](#list-node)) (at least one) | no | On a dashboard: the Readings and Lists this sheet draws. A Tab of a List holds none: Readings stand beside it in the List |\n\n#### Tab open\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `from` | alias | yes | The date a row begins on |\n| `to` | alias | yes | The date a row ends on, empty while it is open |\n\n#### Tab totals\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `rows` | alias | yes | An entity of this model whose rows each belong to one row of the register\'s entity: the rows the columns add |\n| `on` | alias | yes | A date of the child placing each row in time: stored, a formula yielding a date, or a lookup of a date through a one-row link \u2014 never a moment or a span of days |\n| `columns` | list of [Totals column](#totals-column) (1\u20136) | yes | The figures drawn in place of the register\'s columns after each row\'s name, each under its own alias and label |\n\n#### Totals column\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `alias` | alias | yes | The column\'s name: its address in the totals read |\n| `label` | text | yes | What the column holds, in the reader\'s words |\n| `value` | alias | no | A number of the child (stored, a formula or a rollup) summed over the rows; absent, the rows are counted. A figure read in each row\'s own unit or currency stands only where the child\'s select is a lookup, through `via`, of a single select of the register\'s entity |\n| `where` | map of alias \u2192 (list of alias (at least one) \\| boolean) | no | Only the child\'s rows whose select holds one of these options, or whose yes/no is this value \u2014 every entry ANDed |\n| `window` | `"before"` \\| `"in"` \\| `"through"` | yes | Which of the child\'s rows, by `on`, the column adds: `before` those dated before the period\'s first day, `in` those from its first day through its last, `through` those on or before its last day |\n\n#### Period days\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `days` | integer | yes | How many days through today, at most 366 |\n\n<!-- generated:end node-tab -->\n\nA `totals` Tab adds, per column, the child\'s rows `before` the period\'s first day, `in` the period, or `through` its last\nday, so `before` plus `in` of one value is its `through`:\n\n```json\n{\n "type": "Tab",\n "key": "items",\n "label": "Items",\n "totals": {\n "rows": "ledger_line",\n "on": "moved_on",\n "columns": [\n { "alias": "opening", "label": "Opening", "value": "quantity", "window": "before" },\n { "alias": "received", "label": "Received", "value": "quantity", "where": { "kind": ["receipt"] }, "window": "in" },\n { "alias": "issued", "label": "Issued", "value": "quantity", "where": { "kind": ["issue"] }, "window": "in" },\n { "alias": "closing", "label": "Closing", "value": "quantity", "window": "through" }\n ]\n }\n}\n```\n\n## Field node\n\nA Field draws one field of the record it stands in. How it is drawn follows from the field\'s type and the table\'s facts:\n\n- a control or a value, with its notation, money, units and deadline countdown;\n- a files field as pictures and documents, a long or markdown text as prose written in place \u2014 a gallery or a prose block,\n with a heading (`label`) and a condition (`when`) of its own; a run of files Fields is one gallery;\n- a link as its row\'s chip, with a picker narrowed by `options_where`, and `add` for the new row the picker offers;\n- the back side of a child\'s link as the rows filed under the record, added to with `add`;\n- the fields an Act\'s `sign` writes as one signature.\n\n<!-- generated:start node-field -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The field\'s alias |\n| `edit` | `false` | no | false: the field is drawn and read; absent, it is written where the table lets a person write it |\n| `label` | text | no | The heading of the field\'s gallery or prose block, where it draws as one; absent, the field\'s label. A run of files fields takes the first one\'s |\n| `block` | boolean | no | true: a files or text field draws as its own gallery or prose block; false: as a control among the part\'s fields. Absent, files and markdown fields draw as blocks and plain text as a control |\n| `when` | [condition](#app-tree) | no | A gallery or prose block is shown only while the record\'s selects hold these options, its yes/nos these values, its numbers and dates meet their bounds and its `{ "empty": true \\| false }` fields are blank or filled |\n| `add` | `false` \\| `true` \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The new row this field\'s picker offers asks these fields; `true`: the target\'s required fields, its natural key and its title. Stated once per target table, and it holds in every picker of that table in the app. `false`: this link only picks, in every picker of it the app draws, not only where it is written |\n| `record` | list of alias (at least one) | no | The fields of the row this link names that this app saves from the link\'s card, which opens the row in the drawer: stored fields of the linked table a person writes. Stated once per linked table, and it holds on every card of that table in the app, where no List of the app draws its rows. Absent, the card reads the row |\n| `remove` | `true` | no | true: the row this link names is deleted from the link\'s card. Stated once per linked table, with its `record`, and it holds on every card of that table in the app. Absent, the card deletes nothing |\n\n<!-- generated:end node-field -->\n\nA field drawn in the head (title, subtitle, picture, figure, stage) is never also a Field node. A Field is drawn once per\npage. **Writes:** the field, under \xA7 App tree, and with `add` the target table\'s create.\n\n## Act node\n\nAn Act is a verb, stated whole where it stands. Its place decides what it works on:\n\n- in a Section, Side or Page, or in a one-part Record: one record, at that part\'s foot;\n- in a List: on each of its rows, offered in the row\'s \u22EF and, once the row is open, in its header\'s \u22EF; in a child List,\n the child\'s rows;\n- in a composed page\'s `Rows` (\xA7 Composed page): on each row of the child it lists, as in a child List;\n- in a List with `on`: the ticked rows (`"ticked"`) or every row in view (`"view"`), each checked before any is written;\n- directly in a Record that holds parts: refused.\n\n<!-- generated:start node-act -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The act\'s name; its workflow is `act_<key>`, or the `workflow` it names |\n| `label` | text | yes | The verb, as the reader says it |\n| `on` | `"ticked"` \\| `"view"` | no | In a List: the ticked rows, or every row in view; absent, one row |\n| `when` | [condition](#app-tree) | no | The act works only while each named select holds one of these options, each yes/no this value, each number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and each `{ "empty": true \\| false }` field is blank or filled |\n| `requires` | list of alias (at least one) | no | Fields that must be filled first, each drawn where the act stands; a blocked act names each one missing |\n| `recommends` | list of alias (at least one) | no | Fields the act runs without and reads when filled: while one is empty, the act says what it will leave blank |\n| `asks` | list of (alias \\| `{ "field": alias, "starts": "today" \\| "me" }` \\| [Asked input](#asked-input)) (at least one) | no | What the reader states when pressing it: fields of the row (written by the act) or inputs |\n| `set` | map of alias \u2192 [value](#app-tree) | no | What the act writes; each field is the act\'s, read-only elsewhere in the app |\n| `workflow` | alias | no | The workflow the act makes and runs under this name, for what `set` cannot say; no Workflow node is declared for it, and `set_app_workflow` sets its body |\n| `writes` | list of alias (at least one) | no | With `workflow`, fields of the acted row its body writes (an id kept from a service\'s answer) \u2014 the act\'s, as what `set` states is |\n| `template` | alias | no | A document template the act makes a file from: an html one made a PDF, an excel one a workbook, a word one a .docx; over several rows, one file of them all |\n| `templates` | list of [Act template](#act-template) (at least 2) | no | Several papers the act makes into `into`, instead of one `template`: the reader ticks which to make, one press making those |\n| `into` | alias | no | The record\'s files field the made document is kept in; remade, it replaces its own last paper, never a person\'s file |\n| `paper` | text | no | Its paper, `<child>.<kind>`: the line of the record\'s papers it stands on |\n| `intake` | alias | no | A child the record lists whose rows are its papers: the press takes papers, an agent reads them, and each is filed as a row of this child |\n| `fills` | list of text (at least one) | no | With `intake`, what the papers read may fill: a field of the record, `<link>.<field>` on the row a required one-row link names, or a child whose rows the papers add. With `record`, fields of the new row an agent fills from the transcript |\n| `record` | [Recording](#recording) | no | The press records a call, visit or meeting and files it as a new row of a child the record lists |\n| `sign` | [Signature](#signature) | no | The press opens a pad the signer signs on; the act keeps the signature as a picture with who signed, when and the witness |\n| `open` | text | no | The id of another app that reaches this row\'s table, opened at this row by the press: navigation only \u2014 no workflow runs, and a reader without `use` on it is shown that app\'s own refusal. Stated alone with `label` and `when` |\n| `review` | `true` | no | With `workflow`: the workflow only proposes a changeset of the act\'s table, and the reader ticks the rows to write |\n| `confirm` | `true` \\| text | no | The press asks first: the question, or `true` for the act\'s label over the row |\n| `danger` | `true` | no | The act destroys or cannot be undone |\n\n#### Asked input\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `input` | alias | yes | A value the act asks for that is not a field of the record |\n| `label` | text | yes | What the reader is asked, in their words |\n| `type` | `"text"` \\| `"long_text"` \\| `"number"` \\| `"date"` \\| `"select"` \\| `"link"` \\| `"member"` \\| `"file"` | yes | What the reader states. A `file` is one uploaded file, handed to the act\'s authored `workflow` as its file id and read by nothing else |\n| `options` | list of [Select option](#select-option) (at least one) | no | The choices a `select` input offers, one picked \u2014 written into a select holding the same option aliases |\n| `entity` | alias | no | The entity a `link` input picks one row of |\n| `accepts` | list of `"sheet"` \\| `"pdf"` \\| `"document"` \\| `"photo"` (at least one) | no | The kinds of file a `file` input takes; absent, a sheet, a PDF or a document \u2014 a phone offers its camera only where `photo` is among them |\n| `required` | `true` | no | The act is refused without it |\n| `default` | alias | no | A field of the row whose value the input starts from, still changed at will |\n| `add` | `true` \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The new row this picker offers asks these fields; `true`: the target\'s required fields, its natural key and its title. Stated once per target table, and it holds in every picker of that table in the app |\n\n#### Set formula\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `formula` | text | yes | A formula over the acted row\'s fields, `{alias}` each, read as the entity\'s own formulas read them |\n\n#### Act template\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `template` | alias | yes | A document template the act makes a paper of, kept in `into` \u2014 an html one made a PDF, an excel one a workbook, a word one a .docx |\n| `when` | map of alias \u2192 (list of alias (at least one) \\| boolean) | no | The paper starts ticked while each named select of the record holds one of these options and each named yes/no (stored, or a formula \u2014 how a multi-select is tested: `includes({needs}, {needs:x})`) is this value \u2014 a suggestion the reader changes, never a refusal; absent, it starts ticked |\n\n#### Recording\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `into` | alias | yes | A child the record lists in a timeline or rows block, linking back to it by a single link: each recording is filed as a new row of it |\n| `audio` | alias | yes | The child\'s files field the recording\'s audio is kept in |\n| `transcript` | alias | yes | The child\'s text field the transcript is kept in, a speaker\'s turn per line |\n| `video` | alias | no | The child\'s files field the screen is kept in, where the recording captured it |\n| `at` | alias | no | The child\'s date field stamped with the moment the recording started |\n| `by` | alias | no | The child\'s member field set to the person who recorded |\n\n#### Signature\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `into` | alias | yes | The record\'s files field the signature is kept in, as a picture \u2014 each signing replaces the one before |\n| `by` | alias | yes | Who signed: a text field the signer types their name into (someone with no seat in the workspace \u2014 a driver, the owner\'s supervisor), or a member field set to the person pressing, who signs as themselves |\n| `at` | alias | yes | The record\'s date field stamped with the server\'s time as the act runs |\n| `witness` | alias | no | Beside a text `by`, the member field set to the person pressing, who saw the signer sign |\n\n<!-- generated:end node-act -->\n\nAn Act whose `when` does not hold is not offered. One whose `requires` are empty is offered disabled, naming each; one whose\n`recommends` are empty names what it will leave blank and still presses; one a standing Check blocks is offered disabled in\nthe check\'s words. The generated `act_<key>` workflow re-checks all three on the server, writes `set` and what was asked,\nand appends the status history where the act moves the status. A field an act asks is read-only everywhere else. A `select`\ninput offers its own options and is written into a select holding the same option aliases; a `link` input picks one row of\nits `entity`, a `member` input one person. A `file` input is one uploaded file handed to the act\'s authored `workflow` as its\nfile id; nothing else reads it. A field an act both `requires` and `asks` never disables the press.\n\n- **Writes:** every field it sets, asks, files into, signs or records is the act\'s.\n- **Papers.** A button that makes a paper is `template` (or `templates`) with `into` on the Act, with no workflow. `template` makes a document from the row as the act left it and keeps it in `into`; over several rows it\n makes one document of them all. `templates` names a set of papers kept in `into`, each made one as its file and each not\n made yet as a placeholder, those whose `when` holds ticked to start; the press makes the ticked ones, each replacing the\n file its template made before, and the set downloads as one archive (`archive_<key>`). `intake` reads papers: the press\n takes files, an agent reads them, and each is filed as the record\'s line of its kind in the named child; what they state\n of each field `fills` names is shown beside what the row holds, and the save writes only what differs. `sign` opens a pad\n on the device: the act keeps the picture in `into` in place of any before it, `at` stamped with the server\'s time and the\n person pressing in `witness`; the record reads `into` as one signature, and the act\'s paper is handed the picture under\n `into`\'s name (`{{#each signature}}<img src="{{url}}">{{/each}}` in an html template). `record` records a call or a visit\n through the host and files its audio, transcript and screen as a new row of a child.\n- **`workflow`** is an authored workflow (`workflows`) the act runs after its checks, for what neither `set` nor `template` can say.\n **`review: true`** makes it one that only proposes: its body reads, returns a changeset of the act\'s table in\n `data.review`, and the platform writes the rows the reader ticks in the act\'s dialog, each checked first and all written\n in one transaction or none. Over every row in view the act is handed no rows.\n- **A `when` that compares** splits one stage by an amount or a date, with no formula field behind it. The server refuses a\n press the row\'s numbers and dates do not meet, in the words "Approve works only while Amount is at most 5,000,000", and\n a blank number or date meets no bound. Two acts on an expense claim, the second moving it to a stage the Director group owns\n (stage owners decide who presses):\n\n```json\n{ "type": "Act", "key": "approve", "label": "Approve", "when": { "status": ["submitted"], "amount": { "lte": 5000000 } }, "set": { "status": "approved" } }\n```\n\n```json\n{ "type": "Act", "key": "send_to_director", "label": "Send to the director", "when": { "status": ["submitted"], "amount": { "gt": 5000000 } }, "set": { "status": "director_review" } }\n```\n\n```json\n{ "type": "Act", "key": "gate_in", "label": "Make the gate-in EIR", "template": "eir_in", "into": "eir", "sign": { "into": "driver_signature", "by": "driver_name", "at": "signed_at", "witness": "signed_before" } }\n```\n\n## Check node\n\nA Check is a yes/no or text formula of the rows of the List it stands in: it stands while true or non-empty, and a text\nformula\'s words are what the reader sees. Without `on` it only warns; with it, it refuses the saves and acts it names, on\nthe server, in every write it names.\n\n<!-- generated:start node-check -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A yes/no or text formula of the rows of the List it stands in: true or non-empty means the check stands |\n| `on` | `"all"` \\| list of (alias \\| `"update"` \\| `"delete"`) (at least one) | no | What the standing check refuses: `update` is the rows\' saves, an Act key is that act, `all` is every act and save; absent, it only warns. `delete` is this app\'s removal of the rows, for a rule no check of the table states |\n| `resolve` | alias | no | An Act on the same rows that clears it |\n\n<!-- generated:end node-check -->\n\n`update` is the rows\' saves; an Act key is that Act, on the same rows; `all` is every act and save.\nA rule that refuses a Delete is a check of the table, not a Check: state it in the table\'s `checks` with `on: ["delete"]`\n(\xA7 Write rules), where it holds for every way a row is deleted and a removal that cascades to child rows is refused whole.\nA Check naming `delete` refuses only this app\'s removal of a row, and is kept for a rule the table\'s checks cannot state (a\nformula that is neither a yes/no nor a text).\nA Check in a child List stands on each child row. A warning reading the figure of a meter that `gates` marks against its\nbound or its pass mark is refused: the meter already draws it.\n\nThe check field\'s `label` is its short name \u2014 "Over budget", "Release expired": a row\'s line says it in the check\'s tone\n(the most severe of several, and how many more), while its words whole are the row\'s tooltip. Label it as the reader names\nthe problem, in a few words, and let a text formula say the sentence. A check with no `resolve` links to the first field its formula reads that the record draws.\nA yes/no formula whose yes needs attention (papers the customer still owes) is a Check: its yes reads in the warning tone\nwherever it is drawn, every other yes in the success tone \u2014 and the row\'s line says it, so it is no column of the List too.\n\nA Check that every app over a table states the same way restates the table\'s contract and belongs in the table\'s `checks`\n(\xA7 Write rules).\n\nA Check refusing `update` (or `edit: false` on a record, part or field) makes the rows read-only in this app alone, drawn\nunder the read-only mark, never as a lock. A lock is the table\'s: a table workflow, a person, or an Act whose workflow calls\n`lock_records` locks a row, and every door refuses its writes until a table manager unlocks it \u2014 `unlock_records` in an\napp\'s run is judged as the member who pressed. A locked row reads locked in every app: its lock mark, its fields as values\nunder one line naming who can change it, its saves, Delete and each Act writing it refused \u2014 an Act that writes\nelsewhere, makes a paper it hands back, or unlocks it stays its workflow\'s to answer.\n\n## Reading node\n\nA Reading is a number over rows: one figure, its parts, a grid, a figure per period, steps; or a record\'s own figure. The\npicture is the runtime\'s, chosen from what the reading means \u2014 no prop picks a chart. The question is stated by which\nsplit it names:\n\n| Names | Answers |\n|---|---|\n| nothing | one figure: the rows counted, or `value` summed, `{ "avg": \u2026 }` averaged |\n| `by` | the figure per value of a select, one-row link, member or yes/no, or per band of a number\'s `tiers` |\n| `by` a date field | the figure per period (a trend); `ahead: true` reads forward over the rows still to come |\n| `by` and `across` | a grid, `by` down the side and `across` along the top |\n| `steps` | a funnel: 2 to 8 number fields summed, each kept as a share of the one before; in a record only |\n| no `entity`, in a Record | the record\'s own `value`, a tile under its field\'s label; two to eight consecutive tiles draw as one strip, and a single one is a field of the section |\n\n<!-- generated:start node-reading -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The reading\'s query name, unique in the app; a record\'s own figure is keyed by its field |\n| `label` | text | no | Absent, a split\'s `by` field\'s label, a trend\'s summed `value`\'s (else the rows\'), a grid\'s two fields\'; one figure states its label |\n| `entity` | alias | no | The List\'s rows; in a Record, the child\'s rows under it; absent in a Record with no `by`: the record\'s own `value` |\n| `via` | alias | no | The child\'s link to the record, where it has several |\n| `value` | alias \\| `{ "avg": alias }` | no | A number field summed over the rows, or `{ avg }` averaged; absent, the rows are counted |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, whose number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), whose field is `{ "empty": true \\| false }`, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `by` | alias \\| [Tiered filter](#tiered-filter) | no | Splits the rows into parts; a date field splits them per period |\n| `across` | alias \\| [Tiered filter](#tiered-filter) | no | With `by`: the columns of a grid |\n| `over` | alias | no | The date that places a row in time |\n| `ahead` | `true` | no | With a date `by`: the current period and those after it |\n| `steps` | list of alias (2\u20138) | no | Number fields summed, each a step of a funnel |\n| `on_hand` | [Reading on hand](#reading-on-hand) | no | With `by` (and `across`): the Reading\'s rows are what is owed, per value of those fields; drawn beside what is on hand per the same values, and the shortfall |\n| `target` | number | no | The number the figure is read against; absent, its bound, where the table states one |\n| `better` | `"up"` \\| `"down"` | no | Which way is good, for the figure\'s tone; absent, read from the figure\'s shape |\n| `tab` | alias | no | The Tab it reads, else the open one |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n\n#### Reading on hand\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `entity` | alias | yes | The entity whose rows are what is on hand, all of them |\n| `value` | alias | no | A number field summed over those rows; absent, the rows are counted |\n| `by` | alias | no | The field of this entity matching the Reading\'s `by`; absent, the same alias |\n| `across` | alias | no | The field of this entity matching the Reading\'s `across`; absent, the same alias |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, whose number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), whose field is `{ "empty": true \\| false }`, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n\n<!-- generated:end node-reading -->\n\nWhere a Reading stands decides which rows: a dashboard\'s every row, a List\'s the rows in view (so the search, the stage and\nthe filters narrow each one), a Record\'s the child\'s rows under the record. A Reading counts its rows, or sums the number\n`value` names \u2014 never a percent. `over` is a date, the row\'s own or its parent\'s through a lookup: a trend\'s periods, a\nfigure\'s recent periods, and what a dashboard\'s period windows. `where` keeps rows whose select holds one of the options, or\nwhose yes/no is the value, every entry ANDed \u2014 a band leads with the job\'s exception as a formula\'s yes/no (what is\nshort), never a stage the chips already count nor the overdue rows the register\'s summary line counts from `records.due`.\n\n- **A figure\'s change** is green where it moves the good way: a sum\'s rise, a count kept by `where` falling; `better` states\n the other way where that reads wrong (money going out). A value `limits` bounds reads against the sum of its bound\n (`10 / 14`); `target` reads one against a number nothing bounds.\n- **An average** is its sum over the rows that hold a value, divided by how many hold one; a row holding none is no part\n of it, and where none holds one the figure reads a dash, never 0. Under a dashboard\'s period it is the average over the\n period so far, never a sum of averages. A share is an average too: a number formula that is `100` on the rows that count\n and `0` on the others, in percent notation, averaged. Only a single figure averages.\n- **Bands.** `by` and `across` each take, in place of a field, a number by its `tiers` \u2014 `{ "field", "tiers" }` \u2014\n splitting the rows by the bands those breakpoints cut: `[1, 31, 61]` over days late reads not due, 1\u201330, 31\u201360 and over\n 60, the aged report. Every band is drawn in the bands\' own order, one holding no row at zero; rows holding no number\n stand apart. On a List a band is a door: a press narrows the rows to it. In a List\'s band, a split by one or two\n breakpoints stands in it, and one by three or more stands whole under it.\n- **In a List,** Readings stand under the title in one compact band: the job\'s headline number first, then at most the mix\n and the movement. A picture stands only where it splits the rows in view into two parts or more, one holding two rows or\n more. The summary line already totals the figure and the chips count the rows, so a figure summing the figure, or counting\n the rows of a table with a stage, is refused unless its `where` narrows it.\n- **Name.** A Reading\'s `key` is the query it is read by (`run_app_query`).\n\n## Progress node\n\nProgress reads one parent\'s work against its plan \u2014 a List\'s band over a folder, or a record\'s block, never a dashboard\'s\nevery row: the share of the work planned and the share done, cumulative day by day, today\'s two at its foot. Each row is a\npart of the work, its plan spread evenly over its planned days and weighing its `weight` (its value) or, absent, its planned\ndays. Its share done today is `done`, a percent the server computes; on an earlier day, its `done` from its `finished` day\non, before it what its `measured` rows add up to by then.\n\n<!-- generated:start node-progress -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The reading\'s query name, unique in the app |\n| `label` | text | yes | What the progress is of, in the reader\'s words |\n| `entity` | alias | yes | The rows that are the parts of the work: the List\'s, or in a Record the child\'s rows under it |\n| `via` | alias | no | The child\'s link to the record, where it has several |\n| `planned` | [Progress planned](#progress-planned) | yes | The days each row\'s work is planned over, its share planned spread evenly across them \u2014 a row missing either is no part of the plan, and neither line counts it |\n| `done` | alias | yes | A percent field of the row the server computes: its share done today (0\u2013100) |\n| `finished` | alias | no | A date of the row: the day its work was finished, from which it stands at its `done` |\n| `weight` | alias | no | A number of the row: what it weighs in the whole; a row holding none weighs nothing. Absent, each row weighs its planned days |\n| `measured` | [Progress measured](#progress-measured) | no | Before a row is finished, its share done on a day is what these add up to by then, never past its `done`; absent, a row stands at nothing until it is finished, and at its `done` today |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, whose number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), whose field is `{ "empty": true \\| false }`, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `tab` | alias | no | The Tab it reads, else the open one |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n\n#### Progress planned\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | yes | A date of the row: the first day of its planned work |\n| `end` | alias | yes | A date of the row: the last day of its planned work |\n\n#### Progress measured\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `rows` | alias | yes | The entity whose rows record the work as it is done, each through its one-row link to a row of the reading |\n| `on` | alias | yes | A date of those rows (their own, or a lookup): the day each records its work |\n| `share` | alias | yes | A number of those rows the server computes: the share of its row each adds, in percent |\n\n<!-- generated:end node-progress -->\n\n## Statement node\n\nA Statement draws a figure the record works out from its own figures \u2014 a claim\'s payable, an invoice\'s total, a payslip\'s\nnet \u2014 line by line in the order the arithmetic runs: each line a number of the record, the first stating no `op`, the last\nan `equals`. Every figure is a field the server computes or a person writes; the screen adds nothing up. A line\'s `parts`\nlists the rows its figure carries, each read from its own row, and that figure is what they add up to \u2014 a formula, rollup\nor lookup, or the server\'s copy when the record is made (`default_from`), which nothing writes after.\n\n<!-- generated:start node-statement -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The statement\'s name, unique among its siblings |\n| `label` | text | no | Absent, the label of what the statement comes to |\n| `lines` | list of [Statement line](#statement-line) (at least 3) | yes | The figures it is built from, top to bottom, ending in the one it comes to |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n\n#### Statement line\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A number of the record \u2014 stored, a formula, a rollup or a lookup \u2014 read on the line after its label |\n| `op` | `"plus"` \\| `"minus"` \\| `"times"` \\| `"equals"` | no | How the line works on the lines above it \u2014 `plus`, `minus`, `times`, or `equals` for what they come to, a formula, rollup or lookup the server computes; absent on the first line, which the rest work from |\n| `rate` | alias | no | A percent of the record read after the line\'s label: the rate its figure is taken at (retention at 5 %) |\n| `parts` | [Statement parts](#statement-parts) | no | The rows the line\'s figure carries, each listed under it with its words and its own figure \u2014 read from those rows, never a copy of them. The line\'s figure is what they add up to, drawn read: a formula, rollup or lookup, or a value the server copies when the record is made (`default_from`) that nothing writes after |\n\n#### Statement parts\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `rows` | alias | yes | The entity whose rows the line\'s figure carries \u2014 the record\'s own for the earlier rows of the same thing |\n| `same` | alias | yes | A one-link the record and those rows both hold: the rows naming what the record names in it |\n| `at` | alias | no | A moment (a datetime) of those rows no person keys \u2014 when each was certified: with `before`, only the rows whose moment stands before the record\'s, earliest first; absent with it, every row so named, in their own order |\n| `before` | alias | no | A moment (a datetime) of the record no person keys \u2014 when it was made: the moment the rows\' `at` stands before; stated with `at` |\n| `where` | map of alias \u2192 (list of alias (at least one) \\| boolean \\| [comparison](#values) \\| `{ "empty": true \\| false }`) | no | Only the rows whose single select holds one of these options, whose yes/no (stored, or a formula) is this value, whose number or date meets its bounds, or whose field is empty or filled \u2014 read so on the server |\n| `columns` | list of alias (1\u20132) | yes | The rows\' fields read as each one\'s words, in order (its date, its percent) |\n| `figure` | alias | yes | The rows\' number each one reads at the figures\' edge, frozen when the row was certified: a stored number an act fills (`{ "formula": \u2026 }` as it runs) or the server copies when the row is made (`default_from`) \u2014 never one a person keys, nor a formula, rollup or lookup the server recomputes |\n\n<!-- generated:end node-statement -->\n\n```json\n{\n "type": "Statement",\n "key": "claim",\n "lines": [\n { "field": "contract_value" },\n { "field": "done_to_date", "op": "times" },\n { "field": "value_to_date", "op": "equals" },\n { "field": "certified_before", "op": "minus", "parts": { "rows": "claim", "same": "contract", "at": "certified_at", "before": "made_at", "where": { "kind": ["work"] }, "columns": ["claimed_on", "done_to_date"], "figure": "certified_value" } },\n { "field": "claim_value", "op": "equals" },\n { "field": "retention", "op": "minus", "rate": "retention_rate" },\n { "field": "payable", "op": "equals" }\n ],\n "when": { "kind": ["work"] }\n}\n```\n\n## Export node\n\nAn Export is a report file of the rows in view: the title, a line per chip, the search and the moment it was exported, the\nReadings, and every row, at most 20,000. With no props, the one Export is the workbook of the List\'s columns, the Readings\non a sheet before the rows. A List states the reports the business sends \u2014 one a button, several one menu \u2014 each a `label`,\na `description`, and a `template` filled with `title`, `lines`, `readings`, `at` (the moment it was made), `dates` (each\ndate filter\'s `from` and `to` by field) and the rows under the table\'s alias (an html one made a PDF, an excel one a\nworkbook). A report\'s `filename` names its file in the template grammar over the same keys but the rows. Where `per` is a\none-row link, `per_row.<field>` reads a text, number, date or yes/no field of the picked row, and `| add_days:<n>` shifts a\ndate by whole days (`n` a number or such a field, empty for none): `Stock {{per}} {{period.to | add_days:per_row.day_offset | format:"dd.MM.yyyy"}}`. A report of some\nrows (`where`), or of one value the reader picks (`per`: one file per value), is made of the List as those chips would\nnarrow it; the screen keeps the chips and rows it shows.\n\n<!-- generated:start node-export -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | Unique among its siblings; part of the node\'s address |\n| `label` | text | no | The report\'s name; absent on the one Export that stands for the register\'s workbook |\n| `description` | text | no | What the report is for, in a sentence |\n| `template` | alias | no | An html or excel document template the report fills, with `title`, `lines`, `readings`, `at`, `dates`, `period`, `per` and `per_row` (a one-row `per`\'s picked row, the fields `filename` reads); absent, the workbook of the List\'s columns |\n| `filename` | text | no | The file\'s name in the template grammar, over one value each: `title`, `at`, `dates.<field>.from` or `.to`, `period.from` or `.to`, `per`, `per_row.<field>` (a text, number, date or yes/no field of the row a one-row `per` picks), `lines \\| lookup:<n>`; `\\| add_days:<n>` shifts a date by whole days, `n` a number or `per_row.<field>`. Absent, the report\'s label |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `per` | alias | no | A select, member or one-row link the reader picks one value of: one file of that value |\n| `books` | list of alias (at least one) | no | Of the List\'s books, the tables the report reads; absent, all |\n\n<!-- generated:end node-export -->\n\n## Book node\n\nA Book is another table read as part of the register: the same job over its rows \u2014 a legacy ledger, a second branch\'s book.\nA row of the book opens the List\'s Record, lined up by alias; a field the book lacks is blank on its rows and drops from\nwhat they open, and an Act, Check or add needing it is not offered on them. Options line up by alias. The Book\'s own Acts\nand Checks, in its own fields, stand beside the List\'s, each Act\'s `key` its own among the App\'s: an Act of the List running\nits own `workflow`, papers or a call never runs on a book\'s rows, so the Book states its own. With `column: true` the List\ndraws a column naming the table each row is of, and a filter by it. A Book holds the title, the stage, the scope and every\nCheck locking the rows; a table, cards or a plan reads Books.\n\n<!-- generated:start node-book -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `entity` | alias | yes | Another table read as part of the register: the same job over its rows |\n| `fields` | map of alias \u2192 alias | no | A field of the List\'s entity \u2192 the field of this entity holding the same fact, where their aliases differ |\n| `column` | `true` | no | The List draws a column naming the table each row is of, and a filter by it |\n| `row` | [Book row](#book-row) | no | The book\'s rows, stated where they differ from the List\'s |\n| `children` | list of ([Record](#record-node) \\| [Act](#act-node) \\| [Check](#check-node)) (at least one) | no | The Fields a save of the book\'s rows writes (and `edit: false` for none), in a Record as a child List\'s Record states them \u2014 the page a row opens is the List\'s; and the book\'s own Acts and Checks over its rows, beside the List\'s its rows can run |\n\n#### Book row\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `status` | `{ "edit": true }` | no | `{ edit: true }`: the book\'s stage is also moved by hand |\n\n<!-- generated:end node-book -->\n\n**Writes:** the List\'s `remove`, its Fields and its Acts, on the book\'s own table.\n\n## Query node\n\nA Query is a read for agents, never drawn. An app generates a query for everything its screen reads (`agent_surface` lists them); a Query declares one the screen never asks, called through `run_app_query` or the app\'s API: the\ntable whose rows it reads, the fields each row returns or the figure they add up to, and what narrows them.\n\n<!-- generated:start node-query -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `description` | text | yes | What the query answers, read by whoever calls it by its alias \u2014 at most 300 characters |\n| `params` | map of alias \u2192 [Workflow value](#workflow-value) | no | What a call states, by name, each read by an entry of `where` \u2014 a `number`, `boolean`, `date`, `select`, `record_link`, `member`, holding one. `required: false`: the call may leave it out, and the condition reading it narrows nothing |\n| `rows` | alias | yes | The entity whose rows it reads, under that entity\'s read scope. The app\'s own entity is read as its list reads it: kept by the register\'s `where`, and inside the row the app\'s `scope` names, which the call states |\n| `fields` | list of alias (at least one) | no | The fields each row returns, by alias. A query states `fields` or `reading`, one of the two |\n| `sort` | [Sort key](#sort-key) \\| list of [Sort key](#sort-key) (at least one) | no | The order the rows return in; absent, the order the entity\'s list reads in. Only with `fields` |\n| `limit` | integer | no | The most rows returned. Only with `fields` |\n| `reading` | [Query reading](#query-reading) | no | The rows counted or summed instead of returned: one row for all of them, or one per value of `by` or per day of `over`, each carrying its `by` field or `__day`, `__count`, and `__sum` where `value` is stated |\n| `key` | alias | yes | The query\'s name, what `run_app_query` calls; unique in the app |\n| `where` | map of alias \u2192 [condition](#query-node) | no | What narrows the rows, as a List\'s `where` does: a select\'s options, a yes/no\'s value, a number or date meeting its bounds (`gt`, `gte`, `lt`, `lte`), a field `{ "empty": true \\| false }`, `{ "me": true }` on a member field \u2014 or a value the call states (`{ "param": name }`), or a date or number range of params (`{ "from": \u2026, "to": \u2026 }`) |\n\n#### Query reading\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `value` | alias | no | A number field summed over the rows; absent, the rows are counted |\n| `average` | `true` | no | The `value` is averaged: summed over the rows holding one, beside how many hold one. Never with `by` or `over` |\n| `by` | alias | no | The single select, one-row link, member or yes/no each row is counted under \u2014 one row returned per value |\n| `over` | alias | no | A date: one row returned per day of it. Never with `by` |\n\n#### Query param\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `param` | text | yes | A param of this query: the condition holds what the call states for it, and is left out where the call states none |\n\n#### Query range\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `from` | [Query param](#query-param) | no | The param the field is on or after \u2014 a date\'s first day, a number\'s least |\n| `to` | [Query param](#query-param) | no | The param the field is on or before \u2014 a date\'s last day, a number\'s most |\n\n<!-- generated:end node-query -->\n\n```json\n{\n "type": "Query",\n "key": "fees_unbilled",\n "description": "A customer\'s fees no invoice covers yet, oldest first.",\n "params": {\n "customer": { "type": "record_link", "entity": "customer", "description": "The customer whose fees are read." },\n "since": { "type": "date", "required": false, "description": "Only fees charged on or after this day." }\n },\n "rows": "fee",\n "fields": ["visit", "service", "amount", "charged_on"],\n "where": { "billed": false, "customer": { "param": "customer" }, "charged_on": { "from": { "param": "since" } } },\n "sort": { "field": "charged_on" },\n "limit": 200\n}\n```\n\nAn entry of `where` holds what a Reading\'s `where` holds, or a value the call states: `{ "param": "<name>" }`. A required\nparam is always stated; one stating `required: false` may be left out, and the condition reading it then narrows nothing.\nA query reads under its table\'s read scope, as every read of the app does. A `reading` returns one row for all the rows it\nreads, or one per value of `by` or per day of `over`. `set_app` compiles a Query with the rest of the app: no code is set.\n\nEvery Reading of the app is a query too, returning its groups, called by its key. A Tab\'s `totals` read is called by\n`<table>_totals_<tab>` with the period\'s two days, `from` and `to`, both required.\n\nA query reads one table: on an app that states `Book`s, whose list reads several, it names a book\'s own table, never the\nList\'s. A param compares a field holding one value \u2014 or, as a range, a date or a number \u2014 and never a number read in each\nrow\'s own unit or currency, nor a date holding a span.\n\n## Workflow node\n\nA Workflow is a verb for agents, never drawn: a job no press of the screen starts \u2014 an agent or a service handing the app a\nfile, or a list of rows to reconcile a table against. It states what it takes and what it hands back; a table is named by\nits alias in `tables` and a select by `<table>.<field>`. It is called through the app, by `run_app_workflow` or the app\'s\nAPI. On an app shared by a public link it runs only for someone who holds the app, and a visitor\'s call is refused as an\nalias the app does not have.\n\n<!-- generated:start node-workflow -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `description` | text | yes | What the workflow does, read by whoever calls it by its alias \u2014 at most 300 characters |\n| `inputs` | map of text \u2192 [Workflow value](#workflow-value) | no | What a call hands it, by name \u2014 checked before its body runs |\n| `outputs` | map of text \u2192 [Workflow value](#workflow-value) | no | What its body returns as `data`, by name \u2014 checked as it returns; absent, read off the body |\n| `key` | alias | yes | The workflow\'s name, what `run_app_workflow` calls; unique in the app |\n\n#### Workflow value\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `type` | `"text"` \\| `"number"` \\| `"boolean"` \\| `"date"` \\| `"datetime"` \\| `"email"` \\| `"json"` \\| `"record_link"` \\| `"select"` \\| `"member"` \\| `"file"` \\| `"object"` \\| `"array"` | yes | What the value is |\n| `description` | text | no | What the value is for, read by whoever calls the workflow |\n| `required` | `false` | no | false: the value may be left out; absent, it is always there |\n| `multi` | `true` | no | A `record_link`, `select`, `member` or `file` holding several |\n| `entity` | alias | no | The entity a `record_link` names a row of |\n| `field` | text | no | `<entity>.<field>`: the select field whose options a `select` takes, as the field holds them when the workflow runs |\n| `options` | list of alias (at least one) | no | The values a `select` takes, where no field holds them |\n| `fields` | map of text \u2192 [Workflow value](#workflow-value) | no | An `object`\'s values, by name |\n| `items` | [Workflow value](#workflow-value) | no | What each item of an `array` is |\n\n<!-- generated:end node-workflow -->\n\n```json\n{\n "type": "Workflow",\n "key": "reconcile_fees",\n "description": "Reconciles a visit\'s fees against the lines of the carrier\'s statement.",\n "inputs": {\n "visit": { "type": "record_link", "entity": "visit" },\n "statement": { "type": "file", "required": false },\n "lines": { "type": "array", "items": { "type": "object", "fields": { "service": { "type": "select", "field": "fee.service" }, "amount": { "type": "number" } } } }\n },\n "outputs": { "added": { "type": "number" }, "changed": { "type": "number" } }\n}\n```\n\nIts code is set on the app, never stated in the tree: `set_app_workflow` with the app, the key and the `source`, leaving\n`inputs` and `outputs` out \u2014 the code is checked against what the node declares. Every `set_app` keeps that code and holds\nwhat it takes, what it hands back and its description to the node. To change what a workflow takes or hands back, change\nthe node first: `set_app` keeps the code where it still meets the change, and where it does not the workflow stands on no\ncode again until `set_app_workflow` sets one that does. With no code it refuses every call, and each `set_app` says so. A\nWorkflow the tree stops declaring is archived with its key.\n\n## Composed page\n\nA Record whose page is its own arrangement of what the app already states \u2014 a piece of writing under its title, a brief\nbeside its figures \u2014 states `compose` instead of parts: a tree of finished pieces, each named by `use`. A `Rows` holds\nthe Acts and Checks of its rows as `children`, each named by `type`: they are the app\'s own acts and checks on those rows,\nnever pieces of the page.\n\n```json\n{\n "type": "Record",\n "door": "page",\n "compose": {\n "use": "Column",\n "children": [\n { "use": "Bar", "children": [{ "use": "Status" }, { "use": "Field", "field": "audience" }, { "use": "Act", "act": "send_for_review" }, { "use": "Copy", "field": "body", "as": "plain" }] },\n { "use": "Title" },\n { "use": "Document", "field": "body" },\n { "use": "Group", "title": "Reviews", "children": [{ "use": "Rows", "child": "review", "create": false, "children": [{ "type": "Act", "key": "resolve_review", "label": "Resolve", "workflow": "resolve_review", "confirm": true }] }, { "use": "Act", "act": "ask_reviewer" }] }\n ]\n }\n}\n```\n\nA node binds by name: a `field` is a field of the record\'s table, an `act` an Act of the app, a `child` a table linking to\nthe record. What a name binds is read and written exactly as a part naming it would be \u2014 a `Field` and a `Document` are\nsaved through the record\'s own save, an `Act` runs its workflow, a `Rows` reads the child\'s rows \u2014 so a piece adds no write\nof its own; only an Act inside a `Rows` declares one, as an Act of a child List does. A piece holds no condition and no computed value: a piece that does not apply on a row draws nothing, and anything\nthat must depend on the row is an Act\'s `when` or a field\'s formula, never the tree.\n\nThe first `Act` the page itself draws is its primary and every other one secondary; a `danger` act reads so wherever it\nstands. A `Copy` and a `Document` naming one field copy what is on screen \u2014 the words typed and not yet saved among them. The\nrecord\'s chrome stays the runtime\'s: the way back, its comments door and its \u22EF with Delete share the row over the tree, and the\nhead draws no title and no status of its own \u2014 the tree\'s `Title` and `Status` do.\n\n<!-- generated:start apps-compose -->\n\n#### Column piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Column"` | yes | The page\'s reading column, 680px: its pieces stacked in order from one left edge |\n| `children` | list of [piece](#composed-page) (at least one) | yes | The pieces stacked in it, in the order they are read |\n\n#### Bar piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Bar"` | yes | One row of small things on one control height, in two zones: what the record is \u2014 a status, a field\'s control \u2014 from its left edge, what is done to it \u2014 an act, a copy \u2014 ending on its right edge, taking a row of their own under it where the row runs out of room |\n| `children` | list of [piece](#composed-page) (at least one) | yes | Its pieces, each zone\'s in order; a `Field` directly in it is its control alone on one line, named by its label where it holds nothing; an `Act` that destroys or corrects is not drawn here \u2014 it stands in the record\'s \u22EF |\n\n#### Group piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Group"` | yes | A titled stack, headed once by `title` |\n| `title` | text | yes | Its heading, in the reader\'s words; holding one `Rows`, it carries their count and their add, and the group\'s `Act`s end its line |\n| `children` | list of [piece](#composed-page) (at least one) | yes | The pieces under the heading, in order |\n\n#### Panel piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Panel"` | yes | What a reader does not need while writing: nothing in the page, one entry of the record\'s \u22EF named by `title` that opens its pieces in a panel beside the page \u2014 a sheet on a phone; directly in the root `Column` alone |\n| `title` | text | yes | Its entry in the \u22EF and the panel\'s name, in the reader\'s words; an `Act` directly in it ends that name\'s row |\n| `children` | list of [piece](#composed-page) (at least one) | yes | The pieces in the panel, in order: `Rows`, `Field`, `Act`, `Group`, `Document` \u2014 never `Title`, `Status`, `Bar`, `Copy` or another `Panel` |\n\n#### Title piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Title"` | yes | The record\'s title (`records.<entity>.title`) as the page\'s heading, written in place where a person writes it; one a page |\n\n#### Status piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Status"` | yes | The record\'s status (`records.<entity>.status`) as a record\'s head draws it: moved by hand, by its acts\' moves, or read |\n\n#### Field piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Field"` | yes | A field of the record under its label, as a section\'s `fields` draws it |\n| `field` | alias | yes | The field drawn \u2014 never the title or the status, which `Title` and `Status` draw; once a page |\n\n#### Document piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Document"` | yes | A markdown text field written in place with no box, saved as it is written \u2014 whether it is saved said at its foot |\n| `field` | alias | yes | A text field with `format: "markdown"`; once a page |\n\n#### Act piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Act"` | yes | An act of the app on the record as its button: its `when`, what it lacks, its asks and its confirmation as everywhere; the first `Act` the page itself draws is its primary |\n| `act` | alias | yes | An act of this app on the record \u2014 never one `of` a child\'s rows, which stands on its row; once a page |\n\n#### Rows piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Rows"` | yes | A child entity\'s rows under the record, as a rows block lists them, each row\'s own acts on it |\n| `child` | alias | yes | A child entity linking to the record; once a page |\n| `create` | `false` | no | false, rows are not added here \u2014 an act of the page makes them |\n| `remove` | `true` | no | The rows offer a Delete (open row and \u22EF); absent, none |\n| `children` | list of ([Act](#act-node) \\| [Check](#check-node)) (at least one) | no | The acts and checks of its rows, each standing on every row listed, as a child List\'s stand on its rows; absent, the rows offer none |\n\n#### Copy piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Copy"` | yes | A button copying a field\'s words as the page holds them now \u2014 what is typed in its `Document` and not yet saved among them |\n| `field` | alias | yes | The text field copied |\n| `as` | `"plain"` \\| `"raw"` | yes | `plain`, its markdown marks gone; `raw`, as stored |\n\n<!-- generated:end apps-compose -->\n\n## Tasks\n\nA table whose `records` states `"task": true` \u2014 its status a stored select with `closed` \u2014 is work someone finishes.\nEvery List of its rows draws it alike, each row led by a ring. Ticking the ring moves the row to a closed option by the\none write the app already has for that move: the Act of that table setting a closed option, offered on an open row and\nasking nothing, else its own save where people write the status \u2014 stamping what that act stamps and appending the\nstatus history. Unticking runs the Act moving a closed row back, else the save back to the status\'s default. A move no\nwrite reaches leaves the ring disabled and the check notes it. An Act with `on: "ticked"` completes several at once.\n\nA record listing task rows filed under it by a link holding one row says how much of that work is done \u2014 those at a\nclosed option of all \u2014 wherever its row is drawn: a meter on its head, a column of its register\'s rows, a card\'s foot, a\ncalendar entry, a gantt\'s or a lane\'s bar. No rollup is stated for it.\n\n```json\n{\n "tables": [\n {\n "ref": "project",\n "label": "Projects",\n "singular": "Project",\n "fields": {\n "name": { "label": "Name", "required": true, "type": "text" },\n "lead": { "label": "Lead", "type": "member" }\n },\n "title": "name",\n "records": {}\n },\n {\n "ref": "task",\n "label": "Tasks",\n "singular": "Task",\n "fields": {\n "title": { "label": "Title", "required": true, "type": "text" },\n "project": { "label": "Project", "required": true, "type": "link", "table": "project", "multi": false },\n "assignee": { "label": "Assignee", "type": "member" },\n "status": {\n "label": "Status",\n "required": true,\n "default": ["todo"],\n "type": "select",\n "options": [\n { "alias": "todo", "label": "To do", "color": "slate" },\n { "alias": "done", "label": "Done", "color": "green" }\n ]\n },\n "due": { "label": "Due", "type": "date", "format": "date" },\n "done_on": { "label": "Done on", "type": "date", "format": "date" }\n },\n "title": "title",\n "records": {\n "subtitle": ["assignee"],\n "due": ["due"],\n "task": true,\n "starts": { "assignee": "me" },\n "status": { "field": "status", "closed": ["done"] }\n }\n }\n ]\n}\n```\n\n```json\n{\n "body": {\n "type": "App",\n "key": "projects",\n "name": "Projects",\n "color": "blue",\n "tables": { "project": "<id of the project table>", "task": "<id of the task table>" },\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "project",\n "children": [\n {\n "type": "Record",\n "children": [\n {\n "type": "Section",\n "key": "work",\n "title": "Work",\n "children": [\n "lead",\n {\n "type": "List",\n "entity": "task",\n "columns": ["due"],\n "children": [\n { "type": "Act", "key": "complete", "label": "Complete", "when": { "status": ["todo"] }, "set": { "status": "done", "done_on": { "now": true } } },\n { "type": "Act", "key": "reopen", "label": "Reopen", "when": { "status": ["done"] }, "set": { "status": "todo", "done_on": null } },\n { "type": "Act", "key": "complete_all", "label": "Complete", "on": "ticked", "when": { "status": ["todo"] }, "set": { "status": "done", "done_on": { "now": true } } }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\nThe tasks as a register of their own, for one person: a one-part Record holds its Fields and Acts directly.\n\n```json\n{\n "body": {\n "type": "App",\n "key": "my_tasks",\n "name": "My tasks",\n "color": "teal",\n "tables": { "project": "<id of the project table>", "task": "<id of the task table>" },\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "task",\n "opens": { "assignee": { "me": true } },\n "children": [\n { "type": "Reading", "key": "open_tasks", "label": "Open", "where": { "status": ["todo"] } },\n {\n "type": "Record",\n "door": "drawer",\n "children": [\n "project",\n "due",\n { "type": "Act", "key": "finish", "label": "Finish", "when": { "status": ["todo"] }, "set": { "status": "done", "done_on": { "now": true } } },\n { "type": "Act", "key": "undo", "label": "Undo", "when": { "status": ["done"] }, "set": { "status": "todo", "done_on": null } }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Rules of the app\n\nEvery rule the check enforces over an app, one sentence each under an id; a finding names its rule\'s id\n(`[register.filter]`), and `model/<rule id>` is that one rule\'s page. A sentence names the node and the prop an author writes (`List.create`, a `Section`);\neach area stands in the tree where this table says:\n\n| Area | Stands at |\n|---|---|\n| `app` | the App, and the register List\'s props that belong to the app: `words` (labels), `color`, `scope`, Books, logs |\n| `register` | the register List: its columns, filters, search, group, sort, layout, tabs, period, export |\n| `reading` | a Reading or Progress |\n| `record` | a Record and its parts: Section, Side, Page, Act placement |\n| `block` | what a part holds: a child List, a Field gallery or prose block, a Reading, a desk, a timeline or agenda |\n| `statement` | a Statement |\n| `act` | an Act |\n| `check` | a Check |\n\n### App rules\n\n<!-- generated:start rules-app -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `app.alias-unique` | An App\'s `key` is unique among apps, and an Act\'s among the Acts of its App, every List\'s included. |\n| `app.icon` | An App\'s `icon` is a Lucide icon name in kebab-case, as `receipt` or `trending-up` are. |\n| `app.add-label` | A List\'s `add_label` stands only where it adds rows (`create`), in words that are not empty and hold no `{` or `}`. |\n| `app.labels` | An App\'s `words` name a table it reads and a field or option of it, in words that are not empty and hold no `{` or `}`; no two fields of a table, nor two options of a field, show the same words. |\n| `app.records-entry` | Every table an App draws a List over, opens or picks from \u2014 the register\'s, a child List\'s, a linked row\'s, a documents List\'s \u2014 has a `records` entry. |\n| `app.scope` | The App\'s `scope` is a required one-row link of the first List\'s entity, the same on every row the App lists \u2014 never a `columns`, `filters` or `search` entry, `group`, `where`, `opens`, a Field of a part or a `create` question. |\n| `app.when` | A node\'s `when` holds single selects to options, yes/nos to `true` or `false` (stored or a formula of one; a formula with `options` is a select), numbers and dates to bounds (`gt`, `gte`, `lt`, `lte`; a date\'s bound is a day, a moment on a datetime, or `{ "now": true }`) and any field but a yes/no to `{ "empty": true \\| false }` \u2014 a number read in each row\'s own unit or currency and a lookup take no bound. |\n| `app.asked-written` | What a List\'s `create` asks, what an Act\'s `asks` asks and what `starts` fills is a field a person writes \u2014 never a formula, rollup, lookup, autonumber or a date the workspace stamps. |\n| `app.own-rows` | A many-link to rows that each belong to one row of this entity is never a Field of a part, a `create` question or an Act\'s `asks`: those rows are the record\'s own, read as a child List. |\n| `app.create-asks` | A List\'s `create` asks fields a person writes \u2014 never the link or the folder it files the row under, which the add sets itself. |\n| `app.create-required` | A List\'s `create` asks every field its entity requires that nothing else fills \u2014 a `default`, `starts`, the record or folder it files the row under, or a `default_from` over a link it fills \u2014 or the server refuses every add. |\n| `app.books` | A register App\'s Books each name another entity once (`entity`) \u2014 never the App\'s own \u2014 whose fields line up with the register List\'s by alias, else by `fields` (a field the List reads, or one an Export prints \u2192 one of the Book\'s): read as one column type (a day apart from a moment), one value or several alike, a link to the same entity. Each Book holds the title, the status where the List shows one, the folder (`scope`), what `where` keeps and every Check locking the rows; the List is a table, cards or a plan, whose `at`, `across` and `up` line up like any field. Two Books holding a field of their own under one alias hold it as one kind of value. An Export\'s `books` each name the List\'s entity or one of its Books, once, and one of them holds each field its `where` and `per` narrow by. A Book\'s `column: true` draws the column naming each row\'s table in a List that has Books. A Book\'s own Acts and Checks, over its rows in its own fields, stand beside the List\'s: each Act\'s `key` its own among the App\'s Acts, its Books\' and its logs\', and its `workflow` run by no Act of another List or Book. |\n| `app.book-option-kin` | *Noted, never refused:* A Book\'s option labelled as one of the App\'s, under another alias, reads apart from it: the List lists both and counts each on its own. |\n| `app.book-lacks` | *Noted, never refused:* A Book lacking a field the Record, an Act or a `create` reads: what reads it drops from the Book\'s rows \u2014 an Act, a child List, a Section, the `create`. An Act of the List running its own `workflow`, reading papers or recording a call, and an Act of a child the Book\'s record lists no rows of: none is offered on the Book\'s rows, which run the Book\'s own Acts. A field only an Export prints, held by a Book as another kind of value, prints blank on its rows. A Tab\'s `totals` over a child linking to no row of a Book total nothing on its rows. |\n| `app.logs` | A register App\'s logs \u2014 further Lists, at most 3 \u2014 each list another entity under a `key` of their own: never the App\'s own entity, one of its Books\' or another log\'s. A log is one List \u2014 a table or cards, with no Tabs, no Book `column` and no composed Record \u2014 over an entity whose status states no owned `stages`, and stands in an App that states no `scope`. An Act\'s `key` is its own among the App\'s Acts, its logs\' included. No Record of the App or of a log lists the rows of an entity another of the App\'s Lists is over: a link to one opens it on its own List. A List people work as a desk of its own is another App. |\n| `app.rates-in-passing` | *Noted, never refused:* An App with no Act, log or Tabs of its own whose rows another App\'s lines copy a number off (`default_from` through a link) \u2014 the rates a quote is priced from \u2014 is worked only while that record is written: a child List, a Section or an Act of that App, never an App of its own. |\n| `app.row` | A List\'s `row` states only what differs from `records`: a `subtitle` that is not the title, a number for its `figure`, dates for its `due`, an `image` that is a files picture no Act makes documents into, or `false` for a figure, deadlines or picture the entity states and the App draws none of. |\n| `app.tasks` | *Noted, never refused:* A task entity an App lists whose rows no write of the App moves to a closed option (its ring stands disabled), or that an Act ticks done and none moves back. |\n| `app.workflow-value` | A value a Workflow takes or hands back states what its type needs and nothing another type does: a `record_link` its `entity`, a `select` its `options` or the `field` holding them \u2014 never both \u2014 an `object` its `fields`, an `array` its `items`; `multi` stands on a `record_link`, `select`, `member` or `file`; an output is never a `member` or a `file`; and a value nests at most 8 deep. |\n| `app.query` | A Query reads one entity\'s rows as `fields` or as a `reading`, one of the two \u2014 never the App\'s own entity where the App has Books, whose List reads several tables; `sort` and `limit` stand with `fields`; a `reading` splits by `by` or by the day of `over`, never both, and averages only with neither; each param is a number, a yes/no, a date, a select, a one-row link or a member holding one, read by an entry of `where` on a field of its type \u2014 a date by a range, a number by either, and never a number read in each row\'s own unit or currency or a date holding a span. |\n| `app.generated-name` | A name the App generates is taken by nothing the App states. No Query, no Reading\'s or Progress\'s `key` \u2014 a log\'s as it is read, `<entity>_<key>` \u2014 and no Tab\'s read (`<entity>_list_<tab>`) is the name of a read the App generates, of a Query or of a Reading. |\n| `app.surface-described` | *Noted, never refused:* A value a Workflow takes, and a param a Query takes, that states no `description`: the one line a caller reads to fill it. |\n| `app.picks-only-entity` | A Field or a row title that states `add: false` stands in an entity the app reaches. |\n| `app.picks-only-field` | `add: false` stands at a link, once, to an entity whose add the app states. |\n| `app.cards-entity` | A Field or a row title that states `record` or `remove` stands at a link to an entity the app reaches. |\n| `app.remove` | *Noted by the designer on a write, never refused:* An App that writes its register\'s records offers a Delete of them wherever the List writes them, until the List says `remove: false`. |\n\n<!-- generated:end rules-app -->\n\n### Reading rules\n\n<!-- generated:start rules-reading -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `reading.value` | A Reading sums a number, never a percent \u2014 percents do not add up; a value of `{ avg }` reads one. |\n| `reading.average` | `{ avg }` averages a Reading\'s `value` over the rows holding one, read against a plain `target` \u2014 never a value `limits` bounds, whose bound is a sum. |\n| `reading.split` | `by` and `across` split by one value each row holds \u2014 a single select, a one-row link, one member or a yes/no, stored or a formula, a lookup through one-row links as the field it reads \u2014 or by the bands `tiers` cut a number read in one unit on every row; a date `by` splits them per period, as a trend; a grid crosses two different fields. |\n| `reading.over` | `over` is a date: the row\'s own, stored or a formula, or its parent\'s through a lookup over a one-row link. |\n| `reading.where` | A reading\'s and a Query\'s `where` keep rows by a select\'s options, a yes/no\'s `true` or `false`, a number or date\'s bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }`), `{ "empty": true \\| false }`, or a member field holding the reader (`{ "me": true }`); a List\'s `where` also keeps rows by `<link>.<field>`, a select or yes/no of the row a one-row link names; a Query\'s `where` also holds a field to what the call states (`param`, `from`, `to`). |\n| `reading.target` | `target` reads a value nothing bounds \u2014 a value `limits` bounds reads against the sum of its bound. |\n| `reading.better` | `better` needs `over`: a change is read over the periods its date places the rows in. |\n| `reading.list-app` | A List\'s `app` is the id of an app whose first List is over the List\'s own entity. |\n| `reading.progress` | A Progress reads dates of its rows for `planned` and `finished`, a number for `done` and `weight`, and rows `measured` through a one-row link to them, by a date of theirs and a number of theirs. |\n| `reading.alias` | A Reading\'s or Progress\'s `key` is its own among the App\'s Readings and Queries, a log\'s read by its entity before it (`<entity>_<key>`); a Progress above a List reads the List\'s own rows and states no name of its own: its `key` is the one its place gives it, `register_reading_<position>`. |\n| `reading.tabs` | A dashboard with Tabs holds every Reading and List inside a Tab, and every Tab holds one at least; without Tabs, none sits in one. |\n| `reading.restate` | A dashboard\'s count tile never counts the rows a List beside it lists under the same `where` and tab: the List\'s head counts them. |\n| `reading.period` | A dashboard\'s `period` is the one it opens on, and it stands only over a Reading placed in time by `over`. |\n\n<!-- generated:end rules-reading -->\n\n### Statement rules\n\n<!-- generated:start rules-statement -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `statement.lines` | A Statement\'s `lines` are numbers of the record, each once \u2014 the first stating no `op`, every other one, the last an `equals` \u2014 each `equals` a formula, rollup or lookup, and a `rate` a number of the record. |\n| `statement.rows` | A line\'s `parts` read rows of an entity holding `same`, a one-link of the record\'s to the same entity; `at`, a moment of those rows, and `before`, a moment of the record (datetimes, never days), stated together; their `columns` and `figure` are those rows\' fields, the figure a number. |\n| `statement.parts-keyed` | A line\'s `parts` add up to its figure, so no person keys it: a formula, rollup or lookup, a value the server copies when the record is made (`default_from`) that no `create` asks and no save or Act writes, or a stored number the App\'s saves leave out \u2014 the line draws it read; each part\'s `figure` is frozen when its row was certified \u2014 a stored number an Act fills, the server copies or the App\'s saves leave out, never one a person keys or the server recomputes \u2014 and no person keys the moments `at` and `before` either. |\n\n<!-- generated:end rules-statement -->\n\n### Check rules\n\n<!-- generated:start rules-check -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `check.field` | A Check\'s `field` is a yes/no or text formula of the rows of the List it stands in. |\n| `check.of` | A Check stands in the register List or in a child List the Record lists. |\n| `check.not-placed` | A Check\'s field is never also placed on the record as a Field \u2014 the line under the head says it. |\n| `check.blocks` | A Check\'s `on` names what it refuses: `"update"` (the saves), an Act\'s `key`, or `"all"` (every Act and save); a child List\'s Check refuses that row\'s saves and the Acts of it, never the record\'s. It binds this App alone. `"delete"` refuses this App\'s removal of the rows and is kept for a rule no check of the table states: a Delete is the table\'s checks\' to refuse. |\n| `check.resolve` | A Check\'s `resolve` names an Act of this App on the rows the Check stands on, pressed on one row, not refused by the Check, and drawn as a button \u2014 never a correction. |\n| `check.restates-meter` | A warning reading a gated meter\'s figure against its bound or pass mark is refused \u2014 the meter draws it. |\n\n<!-- generated:end rules-check -->\n\n## Register rules\n\nThe rules of the register List\'s props: columns, filters, search, group, sort, layout, tabs, period and export.\n\n<!-- generated:start rules-register -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `register.export-report` | A List\'s Exports each have their own `label`; `per` picks one option, person or one-link row \u2014 never of the status, the folder or a field its `where` fixes. |\n| `register.export-template` | A template an Export fills reads at its root only `title`, `lines`, `readings`, `at`, `dates` and its rows under the List\'s entity alias \u2014 an entity named none of those \u2014 and is the document of no Act. |\n| `register.column-drawn` | A List\'s `columns` never name what the row draws itself \u2014 its title, subtitle, image, status, figure, or the bound its figure\'s meter reads against \u2014 and name a field once. |\n| `register.columns-budget` | A List\'s `columns`, each at the width of its kind, fit the room a desk leaves beside its name and the row\'s own status, deadline and figure. |\n| `register.filter` | A List\'s `filters` name a select, a member, a link, a date, a yes/no or a number of the List\'s own rows \u2014 a lookup through one-row links as the field it reads \u2014 never the status (its chips), nor one `where` fixes. |\n| `register.filter-split` | *Noted, never refused:* A `filters` entry on a field a Reading of the List with `by` or `across` splits the rows by \u2014 a press on its part narrows by it too; a member\'s filter, which narrows to the reader\'s own rows, is never noted. |\n| `register.filter-unshown` | A `filters` entry, a narrowing the List `opens` on, and a press in its band (a Reading\'s part, a grid\'s cell, a figure\'s `where`) name a field its rows show \u2014 a `columns` entry; the row\'s title, the line under it, its picture, status or figure, a meter\'s bound, or the unit or currency a drawn figure is read in; a deadline; a warning its line says; the group or lane it stands under \u2014 or a formula, drawn where every field of its own row it reads is (through other formulas too; one reading no field is drawn nowhere). A narrowing to the reader (`{ "me": true }`) needs none. A tree leaves out a `filters` entry no row shows and says so (name the field as a column or in the row\'s line to offer it); the other two are refused. |\n| `register.tiers` | `tiers` band a number read in one unit on every row; a figure read in each row\'s own unit or currency is named bare. |\n| `register.search` | A List\'s `search` names text, a code, or a link \u2014 matched by its row\'s title. |\n| `register.sort` | Each `sort` key of a List or a Tab names a field of the List\'s rows that holds an order \u2014 never files \u2014 and no field twice. |\n| `register.tabs` | A List\'s Tabs each have a `key` of their own, none the entity\'s alias or one a report template holds (`title`, `lines`, `readings`, `at`, `dates`, `period`, `per`); a Tab states one of `dated` and `open` (dates of its rows, `open`\'s two different) or `totals` (a child\'s rows windowed by the period); its `where` and `sort` hold as the List\'s do, and never narrow by the status, which its chips split. Only a table or cards List has Tabs, and a Reading\'s `tab` names one of them. |\n| `register.tab-totals` | A Tab\'s `totals` name a child linked to the List\'s entity by a one-row link (a child linking the List\'s entity twice cannot be totalled), a date of the child, never a moment or a span, and 1 to 6 columns each under its own alias and label; a column\'s `value` is a number of the child, its `where` holds over the child\'s selects and yes/nos, and a value read in each row\'s own unit or currency stands only where that select of the child looks up, through its link, a single select of the List\'s entity, and the child links to no Book\'s rows. A Book\'s rows are totalled by the child\'s one link to them, by one field to one row; a Book the child links to not at all totals nothing on its rows (`app.book-lacks`). Only a table List has them, and no Tab states `totals` beside `dated` or `open`. |\n| `register.group` | A List\'s `group` names one value per row \u2014 a single select, a one-row link, one member, a date, a yes/no or a text, a lookup through one-row links as the field it reads \u2014 the status too, but never on a task list\'s block, whose chips narrow it; only a table or cards List groups, and a gantt into its lanes; grouping by the title needs a `row.subtitle` to lead each row. |\n| `register.readings-own` | A Reading in a List reads the List\'s own entity; another entity\'s stands on a dashboard. |\n| `register.readings-bands` | A Reading in a List that bands a number by `tiers` has that field with the same tiers among the List\'s `filters`: a press on a band narrows the rows by that filter. |\n| `register.readings-restate` | A Reading with no `by` in a List never sums the row\'s figure, counts the rows, or counts one stage\'s rows, unless it is placed in time (`over`): the summary line totals the figure and the status chips count the rows, but neither draws a change against the last period. |\n| `register.where-opens` | A List\'s `where` holds its own selects, yes/nos, bounds and blanks (a child List\'s or the register\'s), and `opens` its selects and yes/nos and a member field holding the reader (`{ "me": true }`) \u2014 or, on a status whose `stages` have owners, the rows waiting on the reader; a field `where` fixes to options is never a filter nor opened otherwise, and `opens` stays among the options `where` keeps. A List laid out as a `plan` keeps its items by options and yes/nos alone. |\n| `register.roster` | A List with `layout: "roster"` holds the List of its cells: the cells\' entity has one single link to this one and a date on its line (a field of the row itself, or a lookup of its parent\'s date), a roster draws no `columns`, and an `expect` (with `of`) stands only on the cells List. |\n| `register.roster-expect` | A roster cells List\'s `expect` is a single select of its rows, and its `of` this entity\'s multi-select whose every option is one of `expect`\'s. |\n| `register.gantt` | `layout: { gantt }`: `start` and `end` (absent, the entity\'s first `due`) are two different dates of the row, both days or both moments; each diamond (`milestones`) another date of it, each planned date read as `start` is, `progress` a percent and `after` a link to rows of the same entity. |\n| `register.lanes` | `layout: { lanes }`: `by` is a one-row link to an entity with a `records` entry, the rows name a date on their line (a field of the row itself, or a lookup of its parent\'s date), a lane block\'s two ends are read alike (two days or two moments), and `loads` stands with it. |\n| `register.plan` | `layout: { plan }`: `at` is a one-row link to an entity whose `records` states a `plan_place`, `across` and `up` two different numbers of the row, `fill` a single select; a plan List has no Tabs, `group` or `attention`. A write that sets `at`, `across` or `up` is refused unless it clears all three, or places the row at a free cell of a bay (a row from 1 to the bay\'s `across`, a tier from 1 to its `up`, resting on a present item), or at an area with neither. |\n| `register.plan-unique` | *Noted, never refused:* A plan\'s items (the List\'s table and each Book) state `unique` on their place link, `across` and `up`: the app\'s moves refuse a taken cell, and the unique makes every other write hold it \u2014 the note names a table that states none. |\n| `register.plan-mark` | A plan\'s `mark` names a single select of the row and an option of it by alias: a row holding that option says its label on its box and is hatched in the option\'s colour, which the legend names by its label. A Book that holds the select must hold it as a single select with that option; a Book lacking it never marks its rows (`app.book-lacks`). |\n| `register.plan-size` | A plan\'s `size` names a single select of the row and, by option alias, the box (`across`, `along`, `up` \u2014 positive, in the one length unit the places\' `size` states) of each option: a row holding one is drawn as that box, any other at its place\'s cell. A Book that holds the select must hold it as a single select; a Book lacking it draws its rows at their places\' cells (`app.book-lacks`). |\n| `register.plan-box-size` | *Noted, never refused:* A plan whose places state a `size` and whose rows\' select options are ISO 6346 container size-type codes (`20DC`, `40HC`) states the box of each in `size`, from the ISO 668 dimensions, else every box is drawn at its place\'s cell. |\n| `register.loads` | A lanes layout\'s `loads` names one to 2 numbers a lane block adds \u2014 never a percent \u2014 each against a number of the lane\'s row, in one unit or two of one dimension. |\n| `register.band-pivot` | *Noted, never refused:* A Reading with `across` (a grid) among a List\'s Readings: its grid reads on a dashboard, and the band leads with a figure (a Reading with no `by`). |\n| `register.headline` | *Noted by the designer on a write, never refused:* A List over rows that fall due or carry money and have no status whose steps the chips count, laid out as a table, cards or a gantt, has Readings \u2014 they split the rows by a field they show. |\n| `register.span` | *Noted, never refused:* Rows that each span a run of days or hours \u2014 two dates on their line, a `frees`, a `no_overlap` booking them on what its `by` links to, or one required date and the table\'s `due` where the line names no date \u2014 are drawn in time by some List over them (a `layout` of calendar, lanes, gantt or roster) \u2014 the note names the app to add over them, laid out as lanes on what each one books, else as a gantt (a run held by a date and the `due` also asks for both dates on the line). |\n| `register.remove` | A List\'s Delete stands only where the App writes its rows: `remove: true` over rows it cannot write is refused. Absent, rows offer a Delete wherever the List writes them, and `remove: false` takes it away. |\n| `register.remove-roster` | A roster cells List\'s `remove: true` offers the Delete of the roster\'s rows, so it stands only beside a roster over rows the App writes. |\n| `register.attention` | `attention: false` keeps a table\'s or cards\' rows in the List\'s own order \u2014 a first-come queue; a calendar, lanes, a gantt and a roster stand their rows in time and lift none. |\n| `register.unworked-stage` | *Noted, never refused:* A register lists rows (every open one, else those its `where` keeps) at a stage that no Act of the App is offered at and no Section stands at, though the App states the stages it works: the rows only sit in its list, so keep them out with `where` or leave them to the App that works them. |\n\n<!-- generated:end rules-register -->\n\n## Block rules\n\nThe rules of what a part holds: a child List, a Field gallery or prose block, a Reading, a desk, a timeline or an agenda.\n\n<!-- generated:start rules-block -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `block.child-link` | A child List reads a child linked to the record; `via` names the link where the child links more than once. |\n| `block.row-blocks` | The Record of a List below the register \u2014 a rows List\'s, a document\'s under its lines, a roster\'s cells\' \u2014 holds its Fields and the Lists of its rows\' own children, each a child List as one level up over an entity linking to that List\'s, checked against that List\'s entity; nothing else. A List below a row states no `under`, `pick`, `expect` or desk, and a List stating `expect`, `under` or a desk holds no List below its rows. |\n| `block.column-drawn` | A child List\'s `columns` never name what the child\'s row draws (title, subtitle, image, status, figure, or the bound its figure\'s meter reads against) nor its link back, and name a field once; an `expect` List\'s figure is its filled-in-place column. |\n| `block.columns-budget` | A child List\'s `columns`, each at the width of its kind, fit the room a desk leaves beside its name and the row\'s own status, deadline and figure. |\n| `block.title-back` | Where a child\'s title is its link back to the record a child List lists it under, its first other `row.subtitle` names it there \u2014 a text, a code, a select, a link or a member, never a number or a date. |\n| `block.where` | A child List\'s `where` names single selects the child requires, by options they hold \u2014 one held at a single option is never also a column \u2014 and the child\'s yes/nos by value, numbers and dates by bounds, and fields by `{ "empty": true \\| false }`. A yes/no, a bound or a blank is never held: a child List adds rows only at a stored yes/no\'s start, adds none under a bound or a blank, and never picks, expects or stands lines under documents. |\n| `block.under` | `under` names the child\'s one-row link to a document entity that pairs a many-link back and is read under this record by its one link; the lines\' columns never read the document (its heading does) nor hold long text. |\n| `block.under-once` | Documents drawn over their lines (`under`) are never listed again as a List of their own. |\n| `block.edits` | A child List\'s editable columns (`{ "field", "edit": true }`) name at most two of the child\'s columns or its figure that the App\'s saves of the child write \u2014 a number, a date, a few words, one option or a one-row link, never a files or long-text field, the List\'s `via` nor a link it must name alike (a link\'s `same`) \u2014 never on an `expect` List. |\n| `block.remove` | A child List\'s `remove: true` stands only where the App writes those rows. |\n| `block.remove-documents` | The documents List inside a child List with `under` states `remove: true` only over documents the App writes. |\n| `block.compare` | A child List\'s `compare` names two different fields of the child, `before` and `after`: two files fields, or two values of one type (text but long text, a number, a date, a single select). Never with `expect`. Over files on a checklist layout, `before` is the check\'s `photo`. Over values: neither side is a column or something the row draws already (title, line, figure, status, deadline), two numbers share a notation, no column is long text, never on a checklist layout. |\n| `block.current` | A child List\'s `current` names a date or number of the child (stored, or a formula yielding one) that orders a set of revisions: the row holding the latest value is current, the rest fold as superseded. Never with `group`, `under`, `expect`, `checklist`, `compare`, `post` or `pick`, nor beside a column of long text. |\n| `block.checklist` | A child List with `layout: { checklist }` reads a task entity\'s rows as checks \u2014 never under documents, grouped, edited in place or picked together by an Act over several, its columns no files; `photo` names a files field of the child, `clause` a one-row link of it, `failed` options of its status, none `closed`; `passed` a yes/no of it, stored or a formula, setting each mark (yes passed, no failed, empty still to check) and standing without `task` or a status. With `expect` the lines of a standard are the checks, each marked and filled by the block\'s columns (a measured value, a note, a photo), so `photo` and `clause` are not named. |\n| `block.pick` | A child List\'s `pick` links rows of the child through `via` \u2014 an optional one-row link a person writes, no Act writes, no other rule compares rows by and no `under` List stands lines under \u2014 paired with a many-link of the record its own save writes; the child\'s `read_scope` reads a row naming no record through `via`; never with `expect` or `under`, nor where the App\'s saves leave out the child or the many-link. |\n| `block.narrow` | A child List\'s `filters`, `search`, `group` and `sort` follow the register List\'s rules over the child\'s rows, its status among them but on a task list (its chips); never on the link back to the record or a select its `where` holds at one option, nor on an `expect` List; an `under` List is grouped by its documents. |\n| `block.expect` | `expect` names the child\'s title \u2014 a single select or a one-row link a person writes, never the link back \u2014 on a child added in the List; its `columns` name a field filled in place \u2014 unless the List\'s layout is a desk, whose lines are ticked \u2014 and its `create` takes the file where the child is pictured by one. |\n| `block.expect-fills` | An expected line is made from its key and its first filled column \u2014 a desk\'s from its key alone, by its tick \u2014 so every other field the child requires has a default, a start or a `default_from`, or is read first. |\n| `block.expect-of` | An `expect` object\'s `of` or `only` needs a select: `of` names the record\'s multi-select of the title\'s options; `only` names options of a single select, never with `of` or a `where` on it, one List per option; an Act\'s `paper` is drawn in one. |\n| `block.post` | A child List with `layout: { desk }` needs `expect` on a one-row link \u2014 never a select, nor with `under`, `pick`, a checklist layout, `compare` or a `create` list, its line made by a tick from its key alone: its `bodies` are plain text fields of the record a person writes, drawn nowhere else on it; its `text` is a text of the child, stored or a formula yielding text \u2014 a formula reading only stored values of its row, lookups through the link back to the record or through `expect`, and formulas of the row over those, never the clock; its `media` a files field of the record and its `link` a text field of the record; its `network` a single select of the entity the link names, and `networks` holds options of that select, by alias, each to one of facebook, instagram, threads, x, linkedin; its `group` is another single select of that entity. |\n| `block.funnel` | A Reading\'s `steps` (a funnel) are 2 to 8 numbers of its child, each named once \u2014 stored, a formula or a rollup \u2014 all in one notation, never a percent nor a unit read on each row. |\n| `block.list` | A read-only child List (`same`, `app`, or a Record that is `edit: false`) lists the rows of an entity that link to the record by its one link to it (`via` where it has several); with `same`, a one-row link of the record, it lists the rows sharing that link\'s row with the record \u2014 the record left out \u2014 by the listed entity\'s one link to that row\'s entity (`via` where it has several). `same` stands in a Record, never on a dashboard. Its Record\'s Fields \u2014 at least one, each a field of the listed entity named once, never its title nor one of the `columns` \u2014 are what a row shows, read-only, when it is opened in place. |\n| `block.list-opens` | A read-only child List\'s row opens in the record\'s drawer as a record: its entity is one a List of the App draws \u2014 the register\'s, a Book\'s, or a log\'s \u2014 or the List\'s `app` names the register app that works it. |\n| `block.figures` | A Record\'s own figures (Readings with no `entity` and no `by`, whose `value` names the field) are 2 to 8 numbers of the record no person keys \u2014 a formula, a rollup, a lookup through links to one row, or a stored number the App\'s saves leave out \u2014 each placed once on the record. |\n| `block.agenda` | An agenda List\'s child names a date on its row line (`records` title or subtitle); the layout\'s `start` is a date of the record. |\n| `block.timeline` | A timeline List\'s child holds a date to place its entries by. |\n| `block.field-kind` | A gallery Field names a files field of the record, and a prose Field a text field. |\n| `block.read-scope` | A child read under a record whose rows a `read_scope` narrows states its own \u2014 a row rule is not inherited, save the status history\'s, which takes its record\'s. |\n| `block.agenda-note` | *Noted, never refused:* Rows each on a day and within it (at an hour, in a part of the day) listed as a table: a List with `layout: { agenda }` draws them by day. |\n| `block.under-note` | *Noted, never refused:* Rows holding a paper that cover another List\'s lines, listed apart with no add of their own stated: `under` draws them over the lines they cover. |\n| `block.timeline-note` | *Noted, never refused:* Rows each what happened on a day and who \u2014 a date on their line, a member, no status and no figure \u2014 listed as a table: a List with `layout: "timeline"` draws them as a log, newest first. |\n| `block.expect-note` | *Noted, never refused:* Rows each a kind of paper, listed with no `expect` and no `where` on the kind, that no Act files a call into or `fills`: the lines still missing read as what a record needs. |\n\n<!-- generated:end rules-block -->\n\n## Record and act rules\n\n### Record rules\n\n<!-- generated:start rules-record -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `record.placed-once` | A Field, a child List, a Reading, a Statement and an Act each stand once in a Record: its head draws the title, subtitle, image, status, figure and folder, and a field\'s one other place is a part\'s Fields, a gallery or prose Field (`block`) or a Statement \u2014 save the image, which one files gallery may draw again, whole, the figure, which a Statement draws as what it comes to, and rows one List prices and another compares (`compare`); two places whose `when`s never hold together (a select at options the other\'s lacks) each draw it, and a plain child List (no `expect`, `under`, `pick`, `compare`, nor a `layout` of desk or checklist) likewise. |\n| `record.foot-total` | A child List\'s foot totals the child\'s figure and each summed column over the rows it lists; the record\'s rollup summing that field over those rows (the same `where`) is that foot, never also a Field of a part. |\n| `record.fact-restates` | A Field of a part never looks up what its link already reads where it stands: the linked row\'s title beside the link, the figure or bound its meter reads on the head\'s line. |\n| `record.section-holds` | A part (Section, Side, Page) holds Fields, child Lists, Readings, Progress, Statements or Acts. |\n| `record.section-reads` | A Field a part shows as a value only (`edit: false`) is one that part draws. |\n| `record.compose` | A Record\'s `compose` is stated instead of parts (it holds no children), on a page `door`, by an App with no Books whose records have no owned stages (`status.stages`) and no picture (`image`) \u2014 a composed page draws neither. Each name is a field of the entity, an Act of the app on the record or a child entity linked to it; one `Title`, and each field, Act and child drawn once \u2014 a field by `Field` or `Document`, never the title or the status, which `Title` and `Status` draw. The first `Act` the page itself draws is its next step and every other one secondary; an `Act` is a button alone, never one making a set of papers. A `Bar` holds no `Rows` and no `Document`, and an `Act` in it that destroys or corrects what is done (`danger`, or a move back) stands in the record\'s \u22EF instead (the Bar piece\'s own row says how it draws). A `Panel` draws nothing in the page: it is one entry of the record\'s \u22EF, named by its `title`, opening its pieces in a panel beside the page; it stands directly in the root `Column`, holds `Rows`, `Field`, `Act`, `Group` and `Document` \u2014 never `Title`, `Status`, `Bar`, `Copy` or another `Panel` \u2014 and an `Act` directly in it ends the panel\'s name, secondary. A `Group` is headed once, by its `title`: holding one `Rows`, that heading carries the rows\' count and their add in place of the rows\' own, and the `Act`s the group holds end its line. A `Copy` takes a text field\'s words as the page holds them now, a `Document`\'s words not yet saved among them; a field a `Copy` alone names is read, never written. |\n| `record.sections` | *Noted by the designer on a write, never refused:* A List laid out as a table, cards or a gantt opens a Record whose Sections hold the fields people write, a files Field per paper, a child List per child and the record\'s Acts. |\n| `record.header-budget` | *Noted by the designer on a write, never refused:* A Record\'s head says what the row is and where it stands \u2014 its title, line, picture and status \u2014 and at most two more facts, one of them a deadline at most; the rest stands in a Section. |\n| `record.children` | *Noted by the designer on a write, never refused:* A table some App records, whose rows other rows each belong to (a one-row link to it, never its status history), draws some of them under its row \u2014 a child List in its Record, a List with `layout` roster or lanes by that link, or an App over them working inside one of its rows at a time (`scope`). |\n| `record.at` | A Section\'s `at` names options of the record\'s status; a record with no status has no steps. |\n| `record.at-once` | A stage is the work of one Section: two Sections naming one option of the status in `at` hold some Field to values apart in their `when` \u2014 a `when` holding the status keeps its Section off the options it leaves out. |\n| `record.section-page` | A Page stands only on a page `door` \u2014 never a drawer\'s or one beside the List \u2014 is never a step (a Page has no `at`), has a `title` naming an address no other Page shares (a letter or a digit at least), and is never every part. |\n| `record.side` | At most one Side stands in a Record, on a page `door`, and it is never also a Page. |\n| `record.beside` | A Record with `door: "beside"` opens beside a table List \u2014 never a board, cards or a calendar, which draw no list to stand beside. |\n| `record.history` | No part of a Record reads the entity\'s status history (`status.history`): the rail reads each move. |\n| `record.section-acts` | An Act in a part works on the record itself; an Act in a child List stands on the child\'s row, and an Act in the register List with an `on` on the register\'s rows. |\n| `record.act-in-section` | Every Act of the record stands in the part whose work it is; only a correction no part owns \u2014 `danger`, and no other status move offered at the same moment \u2014 or an Act an agent runs for the person (a `workflow` or an `intake` moving no status), placed in the List and in no part, waits in the head\'s \u22EF. |\n| `record.move-home` | A status move offered at one step stands at that step\'s foot when it moves the work forward, to an exit, or back to a step it passes anyway; one offered at several steps, or moving back into a detour, stands in the Section whose `at` holds the status it enters. A tree\'s status move written in a Section with no `at`, or in none, is placed where this puts it and said. |\n| `record.act-staged` | An Act at a staged Section\'s foot is offered only during that Section\'s steps: its `when` holds the status to options the Section\'s `at` names. A tree\'s Act with no `when` at the foot of a Section staged at one step is offered at that step and said; a correction stays offered at every step, and an exit is refused. |\n| `record.act-hidden` | An Act is never offered only while its part\'s `when` hides the part. |\n| `record.outside-place` | An Outside stands directly in a Record whose table states a stored status select, and its `key` is unique in the App: a link to a record names it. |\n| `record.outside-at` | An Outside\'s `at` names options of the record\'s status that no stage owns, and no two Outsides of a record open during the same option. |\n| `record.outside-field` | An Outside\'s Fields are read as values, each a field of the record shown once, never `edit`, `when` or a label of its own. |\n| `record.outside-list` | An Outside\'s Lists read the child rows under the record as they are: their `columns`, `label` and `where` only, never an add, a Delete, a pick, a filter, a search, an `app`, a link that opens, or a bound read from the record. |\n| `record.outside-act` | An Outside\'s Acts are its alone and each ends the visit: `when` holds the status to `at`, `set` moves it to an option outside `at`, `asks` are text, numbers, dates or listed options, `requires` names fields the Outside shows or the act asks, a `sign` is by a text field with no witness, and none states `on`, `of`, `workflow`, `templates`, `paper`, `intake`, `record`, `review`, `open`, `danger`, `recommends` or `me`. |\n| `record.field-edit` | An Act that only writes one field of its record, not the status, from the one input it asks is that field\'s edit: the Field rests in the part as a control. |\n| `record.section-owners` | *Noted, never refused:* A Section holding acts offered at stages that different owners work \u2014 a role apiece, or a member field apiece \u2014 is two jobs in one: each owner\'s part is a Section of its own. |\n\n<!-- generated:end rules-record -->\n\n### Act rules\n\n<!-- generated:start rules-act -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `act.paper-note` | *Noted, never refused:* A made paper kept in a field of a record that lists its papers by kind: it is one of those lines. |\n| `act.moves` | *Noted by the designer on a write, never refused:* Rows a stored status stages towards `closed` options are moved by an Act of some App over them, or by its authored `workflow` \u2014 each open option moves to the next, the last to the first closed one. |\n| `act.set-not-asked` | A field an Act `set`s (an option, a value, `{ "now": true }`, `{ "me": true }`, an input) or `sign`s is the Act\'s outcome: the App\'s `create` never asks it, and a required one takes a `default`. A `formula` only starts a value, which a person may change after. |\n| `act.asks-set` | A field an Act `asks`, `set`s, `sign`s or `writes` is stated by one of them: what the Act writes is its outcome, never also asked, and no field is written by two clauses of one Act. |\n| `act.writes` | `writes` stands beside `workflow` and names stored fields of the acted row, each once, that no `set` (its own or another Act\'s) states. |\n| `act.starts` | `starts` (an ask `{ field, starts }`) starts a field the Act asks \u2014 `today` a date or a moment (never a span), `me` a member \u2014 for the reader pressing it, who changes it at will. |\n| `act.set-formula` | `set: { <field>: { formula } }` reads fields of the acted row and computes what the field holds \u2014 a number, a date, text, a yes/no or a single select\'s option (`{field:option}`), typed as the entity\'s own formulas are \u2014 never the status; a bound a write rule holds the field to is checked on the computed value as the Act runs. |\n| `act.of` | An Act in a child List works on that child: the one row pressed \u2014 or, with `on: "ticked"`, the rows ticked together where the record lists them as tasks \u2014 never the rows in view. |\n| `act.files-written` | An Act\'s `writes` names no files field: a file an Act makes is a paper, filed in a slot it names with `paper` (a child List with `expect`). |\n| `act.writes-line-key` | An Act never writes the link or the key naming an expected line under its record \u2014 a line\'s key is stated by filling it. |\n| `act.makes-or-runs` | An Act makes a document (`template`, `templates`) or runs an authored `workflow`, never both \u2014 the workflow makes what it needs. |\n| `act.does-nothing` | An Act does something: it `set`s a value, `asks` a field, runs a `workflow`, makes a document, or takes an `intake`, a `record` or a `sign`, or `open`s an app. |\n| `act.view` | An Act over every row in view (`on: "view"`) writes (`set`), makes a document or runs a workflow, and states no `requires`. |\n| `act.requires-drawn` | Every field an Act `requires` is drawn where the Act stands \u2014 its part\'s Fields, a child List\'s, its `asks`, or an unstaged part\'s where its own is unstaged \u2014 never the head alone. |\n| `act.requires-written` | A field an Act requires that only an Act writes is written by an Act offered together with it, or asked by the Act itself. |\n| `act.recommends` | A field an Act `recommends` is not also in its `requires`. |\n| `act.set` | `set` writes each field a value it holds \u2014 an option alias, a number, a yes/no, a text, `{ "now": true }` on a date, `{ "me": true }` on a member, `{ "input": "<name>" }` of an input the Act asks of the same kind \u2014 or clears an optional one with `null`. |\n| `act.into` | `into` is a files field of the one record the Act is pressed on, beside its `template` or `templates`. |\n| `act.paper-list` | A List a made paper names is one it can list: none keeps its rows by the reader, and no name is reached by a child List that writes its rows and one that only reads them, or by two of either, at once. |\n| `act.paper` | `paper` is a kind the record lists its papers by, on one record \u2014 never beside `into` or `templates`; one maker per kind. |\n| `act.templates` | `templates` names each template once, kept in `into`, on one record pressed alone \u2014 never beside `template` or `asks`, in a child List, or with an `on` over several rows. |\n| `act.intake` | `intake` names a child the record lists in a child List with `expect` on its title \u2014 a single select, the paper\'s kind \u2014 and is narrowed by no `where`, holding exactly one files field and needing nothing an added line does not fill; the Act runs on one record, states `fills`, and states nothing but its `label`, `when` and `requires` beside them. |\n| `act.record` | `record` names a child the record draws in a timeline List or a child List with no `expect`, `under` or `where`, linking back by one single link, its row needing nothing the recording does not write: `audio` and `video` files fields, `transcript` text, `at` a date, `by` a member, each its own field; on one record, stating only `label`, `when`, `requires` and `fills` beside it. |\n| `act.sign` | `sign` runs on one record, never a child\'s row: `into` a files field, `by` a text field the signer types or a member field set to the person pressing, `at` a date, `witness` a member field \u2014 stated exactly beside a text `by` \u2014 each a field of its own that the Act does not also ask or set, and no other Act signs into `into`. |\n| `act.open` | `open` opens another app at the record and does nothing else: an Act on one record of the app\'s own, stating only `label` and `when` beside it. |\n| `act.open-app` | An Act\'s `open` is the id of an app of this workspace that reaches the record\'s table: a register over it, or a custom-code app declaring no table or declaring it. |\n| `act.fills` | `fills` names each once, only beside `intake` or `record`. Beside `record`: a field of the recording\'s row the recording does not write, never a link. Beside `intake`: a field of the record, `<link>.<field>` on the row a required one-row link names (never beside that link), or a child the record adds rows of in a child List with no `expect`, `under` or `where`, filled in the fields its `create` asks \u2014 every one a new row needs among them. Each field filled is one a person writes \u2014 text, a number, a date, a yes/no, options or a single link\'s row \u2014 never a status, a field an Act writes or a link `same` narrows; a link\'s row is one the App reads of its entity. |\n| `act.fills-drawn` | *Noted, never refused:* A field `fills` writes that the record draws nowhere: the review shows the change, the record does not. |\n| `act.fills-new` | *Noted, never refused:* A child `fills` adds rows of, whose natural key the papers cannot state: every row read is added, so the same papers read twice list them twice. |\n| `act.input` | An input has a name of its own \u2014 never another ask\'s, a field\'s, or the record input its workflow takes (nor `approved` beside `review`) \u2014 `options` exactly when it is a select, `entity` exactly when it is a link, and a `default` field holding what it states. A `file` input is read by the Act\'s authored `workflow` alone, starts empty, and is never written into a field by `set`. |\n| `act.review` | `review: true` stands beside an authored `workflow` on an Act over the record, the ticked rows or every row in view, in an App over one entity \u2014 never in an App with Books, nor beside `set`, `template`, `templates`, `intake`, `record`, `sign` or `confirm`, nor in a child List, and it asks inputs only, none named `approved`. Its workflow is that Act\'s alone, run by no other Act. The workflow calls only steps that read \u2014 no agent \u2014 and returns a changeset of the entity\'s table in `data.review`, the same for the same inputs and rows; the platform writes the rows `approved` names, each only while the run proposes it as the caller read it. Over every row in view (`on: "view"`) it is handed no rows and states no `when`: its workflow reads the rows it may change, whatever the view is narrowed to, and no bound on a run\'s rows applies. |\n\n<!-- generated:end rules-act -->\n\n## What the workspace remembers\n\nThe workspace holds every table, field and select option under an id that never changes, whatever its\nlabel or alias becomes. `set_tables` answers each table\'s id, and an\napp\'s body names tables and templates by those ids, so a relabel on either side is a rename and no body\npoints at another table by accident.\n\nA table `set_tables` refuses to write, or an id the workspace no longer holds, is a refusal by name. `restore_table` puts a deleted table back.\n\n## A complete workspace\n\nTwo jobs, so two apps: a sales desk, where an order is taken and shipped, and an account review, where an account\nmanager reads each customer by tier. The split is per job, never per table: a table a job reads only in passing, such as\na price book, is a picker on that job\'s lines or an Act of that job\'s app (`first_build`, \xA7 3).\n\nOne `set_tables` call makes both tables: each item carries a `ref`, and a link names the table it points\nat by the `ref` of the other item. Its answer carries each table\'s id. One `set_app` call then makes each\napp, `{ "body": \u2026 }`: the tree\'s root names the app by its `key` and `name`, and the tables it uses in `tables` by those ids,\nshown here as placeholders to replace.\n\n```json\n{\n "tables": [\n {\n "ref": "customer",\n "label": "Customers",\n "singular": "Customer",\n "fields": {\n "name": {"label": "Name", "required": true, "type": "text"},\n "logo": {"label": "Logo", "type": "files"},\n "tier": {\n "label": "Tier",\n "default": ["standard"],\n "type": "select",\n "options": [\n {"alias": "standard", "label": "Standard", "color": "slate"},\n {"alias": "gold", "label": "Gold", "color": "amber"}\n ]\n },\n "orders": {"label": "Orders", "type": "link", "pair": "customer", "table": "order", "multi": true},\n "total_ordered": {"label": "Total ordered", "type": "rollup", "via": "orders", "op": "sum", "field": "amount"}\n },\n "views": [\n {\n "alias": "gold",\n "label": "Gold customers",\n "filters": {\n "node_type": "condition",\n "field_key": "tier",\n "type": "select",\n "operator": "has_any_of",\n "value": ["gold"]\n },\n "sort": [{"field_key": "name", "order": "asc"}]\n }\n ],\n "title": "name",\n "records": {"image": "logo", "party": "organization", "figure": "total_ordered"}\n },\n {\n "ref": "order",\n "label": "Orders",\n "singular": "Order",\n "fields": {\n "code": {"label": "Order no.", "type": "text", "unique": true},\n "placed_on": {"label": "Placed on", "type": "date", "format": "date"},\n "amount": {"label": "Amount", "type": "number", "notation": {"style": "currency", "currency": "VND"}},\n "total": {\n "label": "Total with VAT",\n "type": "formula",\n "expression": "{amount} * 1.1",\n "notation": {"style": "currency", "currency": "VND"}\n },\n "customer": {"label": "Customer", "type": "link", "pair": "orders", "table": "customer", "multi": false},\n "note": {"label": "Note", "type": "text"},\n "stage": {\n "label": "Stage",\n "default": ["draft"],\n "type": "select",\n "options": [\n {"alias": "draft", "label": "Draft", "color": "slate"},\n {"alias": "confirmed", "label": "Confirmed", "color": "blue"},\n {"alias": "shipped", "label": "Shipped", "color": "green"}\n ]\n }\n },\n "title": "customer",\n "records": {\n "subtitle": ["code", "placed_on"],\n "figure": "amount",\n "status": {"field": "stage", "closed": ["shipped"]},\n "starts": {"placed_on": "today"}\n }\n }\n ]\n}\n```\n\n```json\n{\n "body": {\n "type": "App",\n "key": "account_review",\n "name": "Account review",\n "color": "blue",\n "tables": {"customer": "<id of the customer table>", "order": "<id of the order table>"},\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "customer",\n "columns": ["tier"],\n "children": [\n {"type": "Reading", "key": "ordered_by_tier", "by": "tier", "value": "total_ordered"},\n {\n "type": "Record",\n "children": [\n {"type": "Section", "key": "account", "title": "Account", "children": ["tier"]},\n {\n "type": "Section",\n "key": "orders",\n "title": "Orders",\n "children": [{"type": "List", "entity": "order", "columns": ["total"], "create": ["code", "amount"]}]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n```json\n{\n "body": {\n "type": "App",\n "key": "sales_desk",\n "name": "Sales desk",\n "color": "violet",\n "tables": {"customer": "<id of the customer table>", "order": "<id of the order table>"},\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "order",\n "filters": ["customer"],\n "create": ["code", "customer", "placed_on"],\n "children": [\n {"type": "Reading", "key": "amount_per_period", "by": "placed_on", "value": "amount"},\n {\n "type": "Record",\n "door": "drawer",\n "children": [\n {\n "type": "Section",\n "key": "order",\n "title": "Order",\n "children": [\n "note",\n {\n "type": "Act",\n "key": "confirm",\n "label": "Confirm",\n "when": {"stage": ["draft"]},\n "set": {"stage": "confirmed"}\n },\n {\n "type": "Act",\n "key": "ship",\n "label": "Ship",\n "when": {"stage": ["confirmed"]},\n "set": {"stage": "shipped"}\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\nThe account review leaves the door a record opens through and the rows it opens in to the system, each the default the\ntable\'s facts give; the sales desk states a drawer. Each List names what its add asks (`create`). The customer\'s orders are a block in its "Orders" section, never its own `Orders`\nlink as a field as well: one fact, one place. An order is titled by its customer and read by its number under it; the\nList names what its add asks, since a subtitle or a figure is never asked by default. `Total ordered` is a rollup, so it\nis read and never asked; the sales desk\'s record opens in a drawer, because its rows are worked one after another. An\norder\'s `stage` is its status (`records.status`), moved only by the app\'s acts: they set it, so the app\'s saves leave it\nout, each act names the stage it is offered at (`when`) and the one it sets, and the register opens on the orders not yet\nshipped (`closed`).\n';
|
|
1764
|
+
var model_reference_default = '# Tables and apps\n\nThe tables of a workspace and the apps over them, each a JSON document. A table is a document, an item of\nthe tool `set_tables`: its fields, options, views, how a row is recognised, and the rules its writes\nfollow. An app is a tree of components, the `body` of the tool `set_app`: one working register over one table, or a\ndashboard of readings. Each \xA7 is its own page, `model/<section>`.\nWhich treatment each job\'s screen needs: the `design` reference. Each treatment worked through in an\napp: the `examples` reference.\n\n**What the documents compose with**\n\n- **Tables and fields** (\xA7 Tables, \xA7 Read scope, \xA7 Field) \u2014 a table\'s columns, options and links. A table document sits\n in the `tables` list of a `set_tables` call; a field sits under its alias in the table\'s `fields`.\n- **Records** (\xA7 Records, then \xA7 Status) \u2014 how a row of each table is RECOGNISED: its title, the line\n under it, its picture, its status, the one number it stands for. Stated once per table\n and read by every surface that draws one of its rows.\n- **Write rules** (\xA7 Write rules) \u2014 what a write finds, bounds and picks among.\n- **Apps** (\xA7 App tree, then a page each: \xA7 Changing an app, \xA7 What an app may write, \xA7 App node, \xA7 List node, \xA7 Layouts, \xA7 Time and place layouts, \xA7 Publish desk,\n \xA7 Record node, \xA7 Section node, \xA7 Side node, \xA7 Page node, \xA7 Outside node, \xA7 Tab node, \xA7 Field node, \xA7 Act node, \xA7 Check node,\n \xA7 Reading node, \xA7 Progress node, \xA7 Statement node, \xA7 Export node, \xA7 Book node, \xA7 Query node, \xA7 Workflow node,\n \xA7 Composed page, \xA7 Tasks, \xA7 App rules, \xA7 Reading rules, \xA7 Statement rules, \xA7 Check rules, \xA7 Register rules, \xA7 Block rules, \xA7 Record rules, \xA7 Act rules) \u2014 one working register over one table and the record each row opens,\n with the logs beside it: which fields go where, the acts, and the checks that guard them \u2014 or a dashboard of readings.\n How each thing LOOKS is the runtime\'s, one treatment per concept; no prop here changes it.\n A screen the job needs and no component states is told to the person \u2014 the job, what the app\n drew, the word wanted \u2014 never props bent to approximate it.\n\n**The working order**\n\n1. The tables and fields the jobs touch, and a `records` entry for every table an\n app lists, opens or picks. Which apps a business needs is `first_build`, \xA7 3.\n2. One `set_tables` call with the tables: a link names the table it points at by the `ref` of another\n item of the call, and the answer carries every table id and the aliases of the fields it added. A\n call carries at most 50 tables, 200 first rows a table, and 2000 rows and 2000 documents in all; more\n tables go in a further call, which names the earlier tables by alias. A call is checked whole and writes\n nothing when refused, listing everything wrong; `dry_run` is for reading the grants a write would make or\n deciding `force`.\n3. One `set_app` call per app. The tree names its tables and document templates by alias wherever it points at one;\n the App node\'s `tables` and `templates` maps are filled by `set_app`, never written.\n `set_app` with the tree as `body` creates the app, named by its `name`; a refused tree writes nothing and is listed whole, and `dry_run` is for reading the permissions the write\n would grant and remove (on the CLI, `lotics app apply <file>`, with `--check` for `dry_run`).\n4. Change an app afterwards one node at a time: `get_app` prints the `tree:`, each node\'s address under `node addresses:`\n and the `version` line, which is the `base_version_id` to send (the tree and addresses go to the app\'s author alone);\n `set_app` with `app_id`, `base_version_id` and `nodes`, a map from a node\'s address to a\n patch, changes it (`model/changing-an-app`), and its answer states the permissions the change grants and removes. A table\n is changed by `set_tables` with its `table_id`, a merge patch over the document `get_table` answers. To go back,\n send the tree you saved before the change as `body` with `app_id`: it replaces `children` but keeps every App prop it\n omits (`words`, `scope`, `period`, `icon`), so null those. (On the CLI, `lotics app pull <app_id>` writes the tree as JSON and `lotics app apply\n <file>` sends an edited file, refused when the app has moved past the version the file was pulled at.) An app\n saved before apps were trees has no tree: `get_app` says so beside what it lets a person write, and `set_app` with\n `app_id` changes it by its whole tree as `body`, which replaces it, or by its name, description, icon and color alone.\n\n## The rules\n\n- **A `set_tables` call is all or nothing.** Every item is checked before any is written; an item\n without `table_id` makes a table, one with it patches that table.\n- **An app\'s labels decide its language.** A body names no locale \u2014 the words an\n app draws itself (its buttons, empty states, refusals) are in the language its\n own labels are written in, so a Vietnamese desk beside English ones answers in\n Vietnamese. The generated screens read the kit\'s\n pack, and a generated WRITE cannot: its refusals run on the server, so they are\n worded in that same language. Mix the two within one app and it answers in two. The app\'s own `words`\n (its words for a table, a field or an option) count as its labels: Vietnamese words over an English table\n make the app answer in Vietnamese.\n- **`set_tables` and `set_app` check all of it before anything is written**, and\n report every problem in one run rather than the first. The tables half of this\n page ends its keys with the rules the check enforces there, and \xA7 App rules and the pages after it hold the app\'s, one sentence each\n under an id; a finding names its rule\'s id (`[register.filter]`), and\n `model/<rule id>` is that one rule\'s page.\n\n<!-- generated:start rules-model -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `model.schema` | Every key is one its table lists, holding the type its row gives, and every required key is stated. |\n| `model.names-declared` | Every alias a key names is declared: a table of the App\'s `tables`, a field of the table the node reads, an option of the select it names, an Act `key` of the App, a template of its `templates`, or an app. |\n| `model.tables` | A call declares at least one table and at most 50; send the rest in a second call. |\n| `model.rows-cap` | Rows are a sample: at most 200 per entity, 2000 per call and 2000 documents attached \u2014 a real data set goes in with `create_records` once the tables exist. |\n| `model.alias-unique` | An alias is unique where it is named: tables, fields and views in their table, options in their select. |\n| `model.label-unique` | A label is unique where it is found by its label: fields and views in their table, options in their select \u2014 and no view takes its table\'s own label, which names the whole-table grid. |\n| `model.template-kind` | An Act\'s `template` is an html, excel or word document template and an Export\'s `template` an html or excel one, never an email or pdf-form one. |\n| `model.template-marker` | Every marker of a Word template an Act makes is filled by that Act: a field of its entity, or a list of the child rows its record lists (the entity\'s own alias over several rows). |\n| `model.template-act` | *Noted, never refused:* In an App none of whose Acts runs an authored `workflow`, every html, excel or word template is made by an Act (`template`, `templates`) or filled by an Export \u2014 the note names the app over the table whose fields it prints most, where the Act belongs. |\n| `model.filter` | A filter \u2014 a view\'s, a rollup\'s \u2014 tests fields of the entity it reads, through links as `entity.field` hops each standing on the entity the last lands on, by options its select declares. |\n\n<!-- generated:end rules-model -->\n\n## Top level\n\nEvery table of keys on this page is generated from the grammar a table document and an app tree are written in, so it is the whole of what a key may hold.\n\n<!-- generated:start top-level -->\n\n#### set_tables call\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `tables` | list of [Table item](#table-item) | yes | One or more, all or nothing; each item is a table document with these keys added. It creates a table (a `label` and `fields` at least) or, with `table_id`, patches one: the document it states merges into the table\'s own |\n| `rows` | map of alias \u2192 list of [Row](#row) | no | First records of the tables the call creates, keyed by each one\'s `ref` |\n| `force` | boolean | no | Consent to losing what rows hold: a field deleted, an option removed |\n| `dry_run` | boolean | no | Answer what the call would change, writing nothing: for reading the grants a write would make or deciding `force`. A refused call writes nothing too and lists every reason, so a dry run checks no more than the call does |\n\n#### Table item\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `ref` | alias | no | A created table\'s handle in this call: other items link to it, and `rows` are keyed by it |\n| `table_id` | text | no | The table to patch; absent, the item creates one. A patch states only what changes: `null` removes a key, a list replaces whole |\n\n<!-- generated:end top-level -->\n\n**A `set_tables` item is a table document** (\xA7 Tables) with `ref` and `table_id` added, and the call\ntakes no other key. **A `set_app` body is an app tree** (\xA7 App tree), whose root is an App node: its `tables` and\n`templates` each map a local alias the tree names to the id of the table or document template it means, from the\n`set_tables` answer or `get_table`. The tree is the only home of the app\'s `name`, `description`, `icon` and `color`;\n`set_app` takes it whole to create, or as a merge patch over the App node with `app_id`, or `nodes` with `app_id` to change one, and with `base_version_id` (the\nversion you read) it refuses a write over a newer one. Nothing is built code. An unknown key is an error, never ignored.\n\nA smallest call, two tables of plain fields, a select and a two-way link \u2014 nothing else is needed to write it:\n\n```json\n{\n "tables": [\n {\n "ref": "customer",\n "label": "Customers",\n "singular": "Customer",\n "fields": {\n "name": {"type": "text", "label": "Name", "required": true},\n "tier": {"type": "select", "label": "Tier", "options": [{"alias": "gold", "label": "Gold", "color": "yellow"}, {"alias": "standard", "label": "Standard", "color": "gray"}]},\n "orders": {"type": "link", "label": "Orders", "table": "order", "multi": true, "pair": "customer"}\n }\n },\n {\n "ref": "order",\n "label": "Orders",\n "singular": "Order",\n "fields": {\n "code": {"type": "text", "label": "Code", "required": true},\n "customer": {"type": "link", "label": "Customer", "table": "customer", "multi": false, "pair": "orders"},\n "amount": {"type": "number", "label": "Amount"}\n }\n }\n ]\n}\n```\n\n### Aliases\n\nEvery `alias` is a lowercase slug \u2014 a letter, then letters, digits and\nunderscores (`unit_price`, `so_1001`). Aliases are how a document and an app tree\nname a field, an option, a view or a table; they are never shown to anyone. `label` is\nwhat a person sees.\n\nLabels must be unique within their namespace \u2014 two fields on one table, two options\non one field, two views on one table cannot share a label.\n\n## Tables\n\n<!-- generated:start table -->\n\n#### Table document\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `label` | text | yes | The table\'s name in the workspace |\n| `singular` | text | no | One row of this table, in the business\'s own words \u2014 what a create\'s button and panel name |\n| `description` | text | no | The table\'s description, written onto the table in the workspace, where a caller of an app\'s queries and workflows reads it beside them |\n| `fields` | map of alias \u2192 [Field](#field) | yes | The table\'s columns, each under its alias, which is its key here and so is never stated inside it |\n| `read_scope` | [Read scope rule](#read-scope-rule) | no | Which rows a member reads. Absent, every member with access to the table reads every row. |\n| `unique` | list (at least one) of list of alias (at least one) | no | Sets of fields whose values no two live rows share \u2014 each a list of field aliases (text, number, date, a single select, or a link holding one row \u2014 `multi: false`); a single text field states `unique: true` on itself instead of a one-field set. A create or update landing a second row with the same values is refused. |\n| `views` | list of [View](#view) | no | Saved views, in the order they are listed; with none, the table still opens on its default grid |\n| `title` | alias | no | The alias of the field that names a row wherever a link or a record shows one; absent, the first text field |\n| `colors` | any value | no | Row colors: a list of { filters, color }, the filter reading fields of this table by alias and a select\'s options by alias |\n| `records` | any value | no | How a row is recognised: the keys of the Record, Status and Cadence tables below but `title`, which sits above; a table they name is an id or a `ref`. A status `history` that names a `ref` no item of the call declares is made by the call as a table, `dates` add each date field they name, and a status with stages each stage and each open option they name none for its `<option>_at`, unless the document declares it; each is listed in the result\'s notes |\n| `natural_key` | list of alias (at least one) | no | The field aliases a row of this table is RECOGNISED by: an app\'s add and find-or-create reuse the row they find by these values, minting one only where nothing matches. A record tool or the API creates the row it is handed. |\n| `no_overlap` | [No overlap](#no-overlap) | no | No two rows hold overlapping spans \u2014 a create or update that would is refused, and so is a change to the dates or states of the row a span is read through that would make two rows overlap |\n| `checks` | map of alias \u2192 table check (`workflows/table-checks`) | no | The conditions a write of the table\'s rows may not meet, each under its alias: a merge patch edits one, and `null` removes it. Expressions name fields by alias |\n| `workflows` | map of alias \u2192 table workflow (`workflows/table-workflows`) | no | The effects that follow every committed write of the table\'s rows \u2014 the grid\'s, an import\'s, an agent\'s, any app\'s \u2014 each under its alias: a merge patch edits one, and `null` removes it. A body names fields, options and tables by alias |\n\n<!-- generated:end table -->\n\n<!-- generated:start rules-entity -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `entity.singular` | A table a List\'s `create` is mounted over \u2014 the register or a child List that adds rows \u2014 states its `singular`: one row of it, in the business\'s words. |\n| `entity.required-cycle` | Required links never wait on each other in a loop: no row of the loop could be created first. |\n| `entity.link-format` | A text field of `format: "link"` holds web addresses; one whose rows hold a phone or a mail address states no format. |\n| `entity.read-scope` | A `read_scope` clause names a group (`member_of`) or a field of its table reached through one-row links: `"self"` a member field, options a single select declares; an `any` or an `all` groups clauses, and groups nest. |\n| `entity.unique` | A `unique` set names two fields or more of its table once each, each holding one value to compare \u2014 text, a number, a date, a single select, a one-row link \u2014 and is stated once; one field alone is `unique: true` on it. |\n\n<!-- generated:end rules-entity -->\n\n**`unique` is a set of values no two live rows share.** Each entry names fields\nof this table holding ONE value \u2014 text, number, date, a single select, a link\nwith `multi: false` \u2014 and a create or update landing a second row with the\nsame values is refused. A set of one text field is that field\'s own `unique:\ntrue`, so it is refused here. A create carries each set in the names its panel\nsends, so the panel can name the duplicate before the write does.\n\n**`singular` is what a create says** \u2014 `New Order`, `Add Claim line`, `H\u1ED3 s\u01A1\nm\u1EDBi` \u2014 while `label` names the table, so without it the button reads `New\nOrders`. Nothing derives it: English plurals are irregular, and no language is\nexempt. In a language without plural forms it is usually the label itself,\nless any word for the collection. A List that leaves `create` to its default adds nothing over a table that states\nnone (nor does the app\'s `words` state one); a `create` list stated over such a table is refused (`entity.singular`).\n\n**An app that only reads a table\'s rows draws them with `edit: false`** and states `create: false` and `remove: false` \u2014 a log of what the system sent, a copy of\nwhat another system holds. The app keeps the section and opens each row at rest, with no Add and no Delete; a party\nof it is picked, never found or minted; and the app writes no create, update or remove for it. It shapes that app, not who may write: a direct write \u2014 the records\nAPI, the chat agent, an import \u2014 follows the table\'s access, as on any table, and another app over the same rows\nstates its own tree.\n\n**`read_scope`** \u2014 which rows a member reads \u2014 is its own page, `model/read-scope`.\n\n## Read scope\n\n<!-- generated:start read-scope -->\n\n#### Read scope rule\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `any` | list of ([Read scope by role](#read-scope-by-role) \\| [Read scope by member](#read-scope-by-member) \\| [Read scope by option](#read-scope-by-option) \\| [Read scope rule](#read-scope-rule)) (at least one) | no | A row is readable when ANY of these holds |\n| `all` | list of ([Read scope by role](#read-scope-by-role) \\| [Read scope by member](#read-scope-by-member) \\| [Read scope by option](#read-scope-by-option) \\| [Read scope rule](#read-scope-rule)) (at least one) | no | A row is readable when ALL of these hold |\n\n#### Read scope by role\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `member_of` | text | yes | The id of a group (from Settings, or chat): whoever is in it reads the row |\n\n#### Read scope by member\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `through` | list of alias (1\u20133) | no | link aliases from this entity outward, each holding one row (`multi: false`) \u2014 the clause\'s field is on the entity the last hop lands on |\n| `field` | alias | yes | A member field alias on this entity, or on the entity `through` lands on |\n| `is` | `"self"` | yes | The members this column names on a row read that row |\n\n#### Read scope by option\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `through` | list of alias (1\u20133) | no | link aliases from this entity outward, each holding one row (`multi: false`) \u2014 the clause\'s field is on the entity the last hop lands on |\n| `field` | alias | yes | A single-select field alias on this entity, or on the entity `through` lands on |\n| `is` | list of alias (at least one) | yes | Its option aliases whose rows are readable \u2014 naming none would hide every row |\n\n<!-- generated:end read-scope -->\n\n**`read_scope` is a ROW rule, enforced by the platform.** It is resolved into the table\'s own row\nfilters, so an app, a workflow reading for a viewer, and the API all answer the same rows \u2014 a per-record\nvisibility field the app merely honours is a convention, not a gate. A `"self"` clause reads a column\nof one member or several, and every group and option a clause names must exist.\n\n**An app reads every table under the table\'s own `read_scope`**, and a tree has no key to lift it. A desk whose audience\nits sharing already decides is let into the rule by a group that holds that audience (`member_of`), stated on the\ntable.\n\n**A row rule is never inherited.** A child read under a private record states\nits own: the record\'s `member_of` clauses as they are, and its column clauses\n`through` the one-row link the child hangs by \u2014 over one hop or several, each a\nlink with `"multi": false`, since over several rows the rule would admit a\nreader any one of them admits while the editor\'s guard reads the first. Only the\nstatus history takes its record\'s rule.\n\n## Field\n\nEvery field carries the keys below, and its `type`\'s section adds the rest; a\ntype whose section names no `default` takes none. `label` may not contain `{` or\n`}` (formulas reference fields by label at the platform level). Every row in `rows` states each\n`required` field it carries (neither a default nor a `default_from` copy is applied to them), and a required\nLINK is written with its row: the table it names is created first, and tables\nwhose required links name each other are refused, since none of their rows could\never be created.\n\n**A table states who keys nothing.** Whether a person may change a field in an app is that app\'s statement\n(what its tree draws, and `edit: false`, \xA7 App tree): a post\'s id a publishing workflow brings back, a count a sync fills, a status an act moves\nare left out of the apps that do not save them, and written by a workflow, the records API, the chat agent and an\nimport as the table\'s access allows. A formula, a rollup, a lookup and an autonumber are no one\'s to key already.\n\n<!-- generated:start field -->\n\n#### Field\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `type` | `"text"` \\| `"number"` \\| `"date"` \\| `"boolean"` \\| `"select"` \\| `"member"` \\| `"link"` \\| `"files"` \\| `"formula"` \\| `"rollup"` \\| `"lookup"` \\| `"autonumber"` | yes | What the field holds \u2014 each type takes the further keys its own section lists |\n| `label` | text | yes | The field\'s name in the workspace |\n| `description` | text | no | The field\'s description, written onto the field in the workspace |\n| `required` | boolean | no | Refuse a record whose cell for this field is empty. Every write path \u2014 create, update, an agent\'s tool call, a workflow\'s set \u2014 refuses the row by field name. A stored field is flagged only once every row holds a value. |\n\n<!-- generated:end field -->\n\n### `text`\n\n<!-- generated:start field-text -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | text | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. |\n| `unique` | boolean | no | Unique values required |\n| `format` | `"text"` \\| `"link"` \\| `"markdown"` | no | How the words are drawn \u2014 plain, as a link that opens, or as markdown |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `suggest` | [Suggest](#suggest) | no | A text field whose control offers, as the reader types, the distinct values a text field of a catalog table holds (a damage position\'s code among the codes on file); any text is still written |\n\n<!-- generated:end field-text -->\n\n### `number`\n\n<!-- generated:start field-number -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | number | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. |\n| `notation` | [notation](#number) | no | How the figure reads: { "style": "decimal" }; money, { "style": "currency", "currency": an ISO 4217 code (USD, VND) }; or a quantity, { "style": "unit", "unit": "percent" (10 reads "10%"), or a measured code (g, kg, t, l, m3, cbm, mm, cm, m, km, m2, min, h, day), which converts and scales within its dimension, or any other noun of at most 12 characters (ki\u1EC7n, pallet, TEU), which never does }. A currency or unit read on each row from a single select of that row is { "per_row": that select }. Omitted, a decimal. |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `min` | number \\| text | no | Refuse a create or update whose value is below this: a constant for a number, or a field \u2014 `<field alias>` of the same row, or `<link alias>.<field alias>` of the one row a link names \u2014 of the same type (a return date after the departure, a line\'s day inside its trip). On a figure whose unit or currency is per row (its notation\'s `per_row`) a constant bound is only 0; bound it by a field of the row |\n| `max` | number \\| text | no | Refuse a create or update whose value is above this: a constant for a number, or a field of the same row or of the one row a link names |\n\n<!-- generated:end field-number -->\n\n`notation` is how the number reads, and every surface reads it:\n`{ "style": "currency", "currency": "USD" }` prints as money in its code,\n`{ "style": "unit", "unit": "kg" }` as a quantity with its unit after it,\n`{ "style": "unit", "unit": "percent" }` as a percent \u2014 10 is "10%", never\nmultiplied \u2014 and `{ "style": "decimal" }`, or no notation, as a plain number. One\nkey, so a figure reads one way.\n\nAn absent number is drawn blank, except a `sum` or count rollup read as a meter\'s measure: nothing accumulated yet reads 0.\n\nA unit \u2014 `{ "style": "unit", "unit": "kg" }` \u2014 is what a figure counts or\nmeasures; `percent` aside, it is measured or counted. A **measured** unit is a code of one catalog \u2014 mass `g`\n`kg` `t`, volume `l` `m3` `cbm` (a cubic metre under freight\'s name), length\n`mm` `cm` `m` `km`, area `m2`, duration `min` `h` `day` \u2014 so a figure typed in\nanother unit of its dimension converts (`12,5 t` into a `kg` field is 12 500),\nand a tile or a chart reads it in the largest unit it reaches (12 500 kg as\n12,5 t\u1EA5n; CBM never scales) while a cell, a fact and a column always read in the\nfield\'s own. Any other noun of at most 12 characters (`ki\u1EC7n`, `pallet`, `TEU`) is a\n**counted** unit: a word after the figure that never converts. A measured unit is\nwritten as its code \u2014 `t\u1EA5n`, `KG` or `m\xB3` is refused, naming the code. A formula\nstates its own as its `notation`; a rollup that keeps the value (`sum`, `avg`,\n`median`, `min`, `max`, `range`) and a lookup carry the unit of the figure they\nread, exactly as they carry a currency, and a count carries none. A quantity is a\nnumber with its unit, never words (`3 cartons` in a text field): only a number\nsums, converts and reads down a column.\n\nA field\'s cells always hold figures in its own unit, so moving it between two\nunits of one dimension (`kg` to `t`) converts every stored figure \u2014 a change made\nin the workspace, in the field\'s settings (which say how many first) or through\n`set_tables`. Any other change of unit\nrelabels.\n\nA figure whose unit or currency varies by row names a single select of its own\nrow as `per_row` \u2014 `{ "style": "unit", "unit": { "per_row": "uom" } }`,\n`{ "style": "currency", "currency": { "per_row": "currency" } }` \u2014 or a lookup of\none through a one-link, a line reading its shipment\'s currency. That select\'s\noptions are the vocabulary: each label a unit (`chi\u1EBFc`, `kg`), or an ISO 4217 code\n(`USD`). A label outside it is refused when the figure is written and when the\nselect is \u2014 its options, its type or its deletion while a figure names it. A row\nwhose select is empty reads its figure bare. Every app reads each row\'s figure in\nits own row\'s unit, and a sum never mixes units: totals are one per unit (past three units a total the runtime draws over a set of rows \u2014\nsummary line, group totals and feet, a dashboard\'s table foot \u2014\nstates the count alone; a reading an author states draws every unit), a chart draws one at a time. A rollup that keeps the value (`sum`, `avg`, `min`\u2026)\nover such a figure stands only where the child\'s select is a lookup, through\nthe rollup\'s own link, of a select on this table \u2014 the rollup reads that\nselect\'s unit; otherwise it is refused. A lookup of such a figure stands only\nwhere its table also looks up the figure\'s select through the same one-link,\nand reads in that lookup; otherwise it is refused. A count is unaffected. Moving a field between a\nfixed and a per-row unit relabels.\n\n### `date`\n\n<!-- generated:start field-date -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | text | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. A date string in the field\'s format. |\n| `format` | `"date"` \\| `"datetime"` \\| `"date_range"` \\| `"datetime_range"` | no | Whether the field holds a day or a moment, alone or as a span |\n| `timezone` | text | no | IANA timezone |\n| `derive_from` | `"created_at"` \\| `"updated_at"` | no | Auto-populate from the row\'s system timestamp; the field becomes read-only. |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `min` | number \\| text | no | Refuse a create or update whose value is below this: a constant for a number, or a field \u2014 `<field alias>` of the same row, or `<link alias>.<field alias>` of the one row a link names \u2014 of the same type (a return date after the departure, a line\'s day inside its trip). On a figure whose unit or currency is per row (its notation\'s `per_row`) a constant bound is only 0; bound it by a field of the row |\n| `max` | number \\| text | no | Refuse a create or update whose value is above this: a constant for a number, or a field of the same row or of the one row a link names |\n\n<!-- generated:end field-date -->\n\nA `default` is refused beside `derive_from`: the platform stamps that date.\n\n### `boolean`\n\n<!-- generated:start field-boolean -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | boolean | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n\n<!-- generated:end field-boolean -->\n\n### `select`\n\n<!-- generated:start field-select -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default` | list of alias | no | Value pre-filled into a new record when none is supplied for this field. Applied on create only \u2014 existing records are never backfilled. Option alias(es) this field declares \u2014 one for single-select. |\n| `options` | list of [Select option](#select-option) (at least one) | yes | The choices, in the order every picker and every ladder lists them |\n| `multi` | boolean | no | Allow multiple selections |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n\n#### Select option\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `alias` | alias | yes | Stable local alias, unique within the field |\n| `label` | text | yes | Display label for the option |\n| `color` | [colour](#select) | no | The colour the option\'s badge is drawn in; absent, the platform gives it one no sibling option holds |\n| `mark` | [mark](#select) | no | The option\'s own mark, drawn in place of its colour dot wherever the option is shown: the brand it is ({kind: "brand", name: one of facebook, instagram, threads, meta, tiktok, google-ads, zalo, linkedin, x, google-meet, youtube, telegram, whatsapp, gmail, google-drive, outlook, kiotviet, misa, lark, payos}) a kit glyph ({kind: "icon", name: "wrench"}) or the face of who it is, drawn from its label ({kind: "avatar", name: "person" or "organization"}). Every option of a field has one, or none does. A mark a reader does not draw falls back to the dot |\n\n<!-- generated:end field-select -->\n\nA patch of a select states its whole `options` list: a list replaces whole, so an option it leaves out is deleted. To\nrename or recolour one option, restate every option kept, each with its alias. Rows still holding a deleted option refuse\nthe patch unless `force`.\n\n```json\n{\n "options": [\n { "alias": "draft", "label": "Draft" },\n { "alias": "confirmed", "label": "Confirmed" },\n { "alias": "shipped", "label": "Shipped", "color": "green" }\n ]\n}\n```\n\n`color` is one of: `red`, `orange`, `amber`, `yellow`, `lime`, `green`,\n`emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`,\n`fuchsia`, `pink`, `rose`, `slate`, `gray`, `zinc`, `neutral`, `stone`.\nEvery option is drawn in its colour wherever it shows \u2014 a cell, a row\'s line,\na record, a picker, the bar a reading splits the rows by.\n\nAn option may carry its own `mark`, drawn in place of its colour dot wherever\nthe option is shown \u2014 a stage, a chip, a filter, a fact, an entry of a log, a\nreading\'s part, a lookup of the select on another table. Where the select is its\nrows\' `records` `mark`, a brand or glyph stands on the row\'s lead tile in the option\'s\ncolour, and an option with none leads with its colour dot there. A select a reader scans\ndown a column \u2014 how a payment was made, the channel, the mode \u2014 states one on every\noption, since a glyph reads before its word: the brand it IS\n(`{"kind": "brand", "name": "tiktok"}` \u2014 one of `facebook`, `instagram`,\n`threads`, `meta`, `tiktok`, `google-ads`, `zalo`, `linkedin`, `x`,\n`google-meet`, `youtube`, `telegram`, `whatsapp`, `gmail`, `google-drive`,\n`outlook`, `kiotviet`, `misa`, `lark`, `payos`), a glyph the kit draws\n(`{"kind": "icon", "name": "wrench"}`; any other name is refused), or \u2014 where the\noptions name people or organisations who are no member and no row \u2014 the face of who\nit is, drawn from its label (`{"kind": "avatar", "name": "person"}` or\n`"organization"`). Every option of a select has one, or none does: a run of chips\nwhere one carries no mark reads as the one missing something. `set_tables` writes the\nmarks onto the table. The glyph names are `model/field/option-glyphs`.\n\n```jsonc\n"options": [\n { "alias": "short_video", "label": "Short video", "color": "zinc", "mark": { "kind": "brand", "name": "tiktok" } },\n { "alias": "print", "label": "Print", "color": "amber", "mark": { "kind": "icon", "name": "newspaper" } }\n]\n```\n\n### Option glyphs\n\nThe kit\'s glyph names an option\'s `mark` takes:\n\n<!-- generated:start field-select-glyphs -->\n\nactivity, align-center, align-left, align-right, align-vertical-justify-center, align-vertical-justify-end, align-vertical-justify-start, arrow-down, arrow-down-a-z, arrow-down-up, arrow-down-wide-narrow, arrow-left, arrow-left-from-line, arrow-right, arrow-right-from-line, arrow-right-left, arrow-up, arrow-up-down, arrow-up-wide-narrow, arrow-up-z-a, ban, banknote, bed, bell, between-horizontal-start, between-vertical-start, bold, bolt, book-marked, book-open, book-text, bot, box, brackets, brain, briefcase, building-2, calculator, calendar, calendar-clock, calendar-off, camera, car, chart-column, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down-up, chevrons-up-down, circle-alert, circle-check, circle-x, clipboard-list, clipboard-paste, clock, code, code-xml, columns-3, columns-3-cog, combine, construction, container, copy, credit-card, database, decimals-arrow-left, decimals-arrow-right, dollar-sign, download, ellipsis, eraser, expand, external-link, eye, eye-off, facebook, file, file-csv, file-down, file-question, file-spreadsheet, file-stack, file-text, file-up, folder, folder-closed, folder-open, folder-pen, form, funnel-plus, funnel-x, gauge, globe, gpu, grid-2x2, grip-vertical, group, hand-coins, hash, heading, heading-1, heading-2, heading-3, history, house, image, inbox, info, instagram, italic, keyboard, languages, layout-dashboard, layout-grid, library-big, link-2, link-2-off, linkedin, list, list-checks, list-collapse, list-filter, list-filter-plus, list-ordered, list-tree, loader, lock, lock-keyhole, lock-keyhole-open, lock-open, log-in, log-out, mail, map-pin, maximize-2, megaphone, menu, message-circle, message-circle-question-mark, message-square, messages-square, mic, minimize-2, minus, monitor, mouse, mouse-pointer-click, music, newspaper, notepad-text-dashed, package, paint-bucket, paintbrush-vertical, palette, panel-left, panel-left-close, panel-left-open, panel-right, panel-right-close, panel-right-open, paperclip, pause, pencil, percent, phone, pin, pin-off, plane, play, plug, plus, receipt, rectangle-ellipsis, redo, refresh-cw, repeat, rotate-ccw, rotate-cw, rows-3, scan, scan-line, scissors, search, send, settings, share, share-2, shield, shield-alert, shield-check, shopping-cart, sigma, sliders-horizontal, smile, smile-plus, snowflake, sparkles, split, square, square-check, square-pen, square-sigma, stethoscope, sticky-note, strikethrough, table, table-2, tag, target, text-quote, text-wrap, thumbs-down, thumbs-up, ticket, trash, trending-down, trending-up, triangle-alert, truck, tv-minimal, twitter, type, underline, undo, upload, user, user-check, user-pen, users, utensils, waypoints, workflow, wrench, x, zap, zoom-in, zoom-out\n\n<!-- generated:end field-select-glyphs -->\n\n### `member`\n\nA person picker over the workspace\'s members. No default. With no role it is still drawn \u2014\nface and name, ranked as a `party` \u2014 in a screen\'s `columns` and in the register\na record draws of these rows.\n\n<!-- generated:start field-member -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `multi` | boolean | no | Allow multiple selections |\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n\n<!-- generated:end field-member -->\n\n### `link`\n\n<!-- generated:start field-link -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `default_from` | text | no | Copied when a record is made off the row a one-row link of this entity names \u2014 `<link alias>.<field alias>` \u2014 or, reading on through one-row links, off the row the last of them names \u2014 `<link alias>.<link alias>.<field alias>` \u2014 and never re-read, so the source changing later leaves the row alone. A value the add states wins; a select copies each option as this field\'s option of the same alias. |\n| `options_where` | [filter](#views) | no | Which rows of the target this link may point at \u2014 an `and` group of plain conditions over the TARGET table\'s own fields, each `field_key` a field alias. It narrows every picker of the link, and a save naming a row outside it is refused on every path \u2014 an app, the grid, an import, a workflow, the API. |\n| `same` | list of alias (at least one) | no | One-row links this row and each row this link points at must name alike \u2014 a fee\'s invoice is one of its own visit\'s, a box\'s seal one of its own shipping line\'s: each a one-row link of both tables to the same table. It narrows the picker\'s read to the rows naming what this row names, a row made from the picker names it too (set, never asked), and a write is refused wherever it lands \u2014 on the link, or on what the two rows compare. |\n| `table` | text | yes | The table the link points at: its alias, or the `ref` of another item of the call |\n| `multi` | boolean | yes | true: the link holds many rows; false: one. Where the link is paired, at most one of the two sides holds one |\n| `pair` | `true` \\| alias | no | Makes the link two-way: the alias of its partner field on the other table, which names this one back, or `true` to have the alias derived. Stated on one table, the partner is made on the other with the inverse cardinality; a many-to-many link states both sides |\n\n<!-- generated:end field-link -->\n\n**`multi` is stated on every link:** `true` holds several rows, `false` one \u2014 one customer on an\ninvoice, one project on a device. There is no default.\n\nA two-way link states `pair` on one table, as `true` or a free alias, and the partner is made on the\nother table with the inverse cardinality; a many-to-many link states both sides, since the inverse of\nmany is not decided. A pair stated on both sides is made once. Both tables are written in the one\ncall (`table` names the other item\'s `ref`). A link with no `pair` is one-way, and nothing is created on the target. Pair a link when\nthe target\'s own record should list what points at it, and leave it unpaired when it should not: either\nway the record plan draws the relation as a section on the side it points at, so an unpaired link costs\nthe target nothing. Of two paired links at most one holds one row.\n\n```jsonc\n// a field of table "order"\n"customer": { "type": "link", "label": "Customer", "table": "customer", "multi": false, "pair": "orders" }\n```\n\n```jsonc\n// a field of table "customer"\n"orders": { "type": "link", "label": "Orders", "table": "order", "multi": true, "pair": "customer" }\n```\n\n### `files`\n\nA row attaches documents to it (\xA7 Rows). Each file\nis drawn by its kind, with no key to choose: a picture (an image, a video) as its\nthumbnail, a document (a PDF, a sheet) as its type\'s badge and its filename.\n\n### `formula`\n\nA formula states its keys on the field itself, beside `type` and `label`:\n`{ "type": "formula", "label": "Total", "expression": "{quantity} * {unit_price}", "notation": { "style": "currency", "currency": "USD" } }`.\n\n<!-- generated:start field-formula -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `expression` | text | yes | The expression, over fields of THIS entity by alias in braces \u2014 `{quantity} * {unit_price}` |\n| `output_type` | `"number"` \\| `"text"` \\| `"date"` \\| `"datetime"` \\| `"boolean"` \\| `"select"` | no | Absent, it is what the expression yields. Stated, it is checked against that and refused where they differ (a datetime is never stated date) |\n| `notation` | [notation](#number) | no | How the figure reads: { "style": "decimal" }; money, { "style": "currency", "currency": an ISO 4217 code (USD, VND) }; or a quantity, { "style": "unit", "unit": "percent" (10 reads "10%"), or a measured code (g, kg, t, l, m3, cbm, mm, cm, m, km, m2, min, h, day), which converts and scales within its dimension, or any other noun of at most 12 characters (ki\u1EC7n, pallet, TEU), which never does }. A currency or unit read on each row from a single select of that row is { "per_row": that select }. For a formula yielding a number. |\n| `link` | `true` | no | The formula yields a URL as text, drawn as a link that opens. |\n| `options` | list of [Select option](#select-option) (at least one) | no | The categories the formula yields, drawn as a single select\'s options are (read-only). The expression yields one of them as `{this_field:option}`, or null \u2014 `{days_idle} > 30 ? {warmth:cold} : {warmth:hot}`. Omit for a formula yielding a plain value. |\n\n<!-- generated:end field-formula -->\n\n`output_type` is what lets a role or a screen clause accept a computed value: a\ncaption over a derived name (`output_type: "text"`), a period over a settled date\n(`"date"`). A formula declaring no `output_type`, `notation` or `link` says nothing\nabout its result, and every rule that needs one refuses it by name. A formula stating\n`options` is a computed category: read-only, and read as a single select\nwherever it is drawn \u2014 a column, a filter, a reading\'s `by` or `where`.\n\n**A select reaches a formula as the KEYS of its chosen options**, a list \u2014\nnever their labels, and never their aliases \u2014 and a document has no keys: the\nworkspace mints them when the table is made. So an option is named in a formula\nas `{field:option}`, both aliases, and the write puts that option\'s key in its\nplace: `includes({kind}, {kind:crate})` for a select holding one or several,\n`{kind}[0] == {kind:crate}` for a single one. A select compared to its own words\n(`{kind} != "Crate"`) matches no row and computes the other branch everywhere,\nso the check refuses it and names the token. A select looked up from another\ntable is tested there, in a formula of its own, and that result looked up.\n\n**The language.** The check refuses a call or a name the platform cannot run, naming what to write:\n\n<!-- generated:start field-formula-language -->\n\n- Operators: `+ - * / %`, `== != > < >= <=`, `&& || !`; `+` also joins text\n- Numbers: arithmetic is exact decimal (`0.1 * 3` is `0.3`), a division that does not end the nearest double; a comparison between two numbers reads both at 15 significant digits; `round` rounds a half away from zero (`round(-2.5)` is `-3`)\n- Conditionals: a ternary only \u2014 `{amount} > 100 ? "High" : "Low"`\n- Not supported: optional chaining (`?.`), nullish coalescing (`??`), template literals, arrow functions \u2014 use `get(obj, "path", default)`, `coalesce(v1, v2)`\n- Helpers are these names, spelled exactly; a spreadsheet function (`IF`, `SUM`, `LEN`, `DATEDIF`) is none of them, and a formula calling one is refused\n- Math: round(n,decimals?), ceil(n), floor(n), abs(n), min(a,b), max(a,b), sum(arr), mean(arr), clamp(n,min,max), percentage(part,total,decimals?), pow(base,exp), sqrt(n), mod(n,divisor)\n- Strings: upper(s), lower(s), trim(s), capitalize(s), length(s), contains(s,search), join(arr,sep), split(s,sep), replace(s,search,rep), replaceAll(s,search,rep), removeChars(s,chars) (s without any character that occurs in chars), removeSpaces(s) (s without any whitespace), startsWith(s,prefix), endsWith(s,suffix), substring(s,start,end?), padStart(s,len,char), padEnd(s,len,char), numberToWords(n, lang?) (lang \'vi\' default, or \'en\'), uuidFrom(s) (the same UUID for the same text)\n- Lists: includes(list,value), first(list), last(list), unique(list), compact(list) \u2014 length(list) counts one\n- Dates: now(), formatDate(d,fmt), addDays(d,n), subDays(d,n), addHours(d,n), subHours(d,n), addMinutes(d,n), subMinutes(d,n), startOfDay(d), endOfDay(d), differenceInCalendarDays(later,earlier), differenceInHours(later,earlier), differenceInMinutes(later,earlier), isBefore(d1,d2), isAfter(d1,d2), isSameDay(d1,d2), isToday(d), isWithinRange(d,start,end), meetsBounds(value,kind,{gt,gte,lt,lte},tz?), parseDate(d)\n- Null/type: isNull(v), isEmpty(v) (also true for "" and []), coalesce(v1,v2,...), isString(v), isNumber(v), isBoolean(v), isArray(v), toNumber(v), toFigure(v), toString(v)\n- Other: formatCurrency(amount,locale,currency), formatDecimal(value,decimals,locale) (grouped quantity, no symbol), get(obj,"path",default?)\n- Empty cells: a cell nobody filled is null inside a formula, whatever its type; one holding 0, false or "0" is not empty. Test it with `isEmpty({note})` \u2014 `{note} == ""` and `{done} == false` are false on an unset cell\n- Arithmetic over an empty cell: `+` and `-` read it as 0 beside a value (`{fee} + {surcharge}` is `{fee}` when the surcharge is empty, null when both are); `*`, `/`, `%` and a unary `-` yield null (`{price} * {qty}` is null, not 0, when the quantity is empty)\n- No helper throws on an empty cell: the math helpers and toNumber return null, the string helpers "". A value of the wrong type still errors. When EVERY field a formula reads is empty it is null \u2014 unless it reads each only as the argument of isEmpty, isNull or isNotNull (`!isEmpty({file})` is false there, not null)\n\n<!-- generated:end field-formula-language -->\n\n### `rollup`\n\nAggregates the records reached through a link on this table.\n\n<!-- generated:start field-rollup -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `filter` | [filter](#views) group | no | Only linked records matching this filter are aggregated; one condition on its own is a group of one. Every `field_key` in it names a field alias on the linked entity, and a select condition\'s value names an option alias there. A traversal node reaches past that entity, so its `path` hops and inner `field_key` are fully-qualified `entity.field` aliases. |\n| `via` | alias | yes | The alias of a link of this table to roll up from |\n| `op` | [operation](#rollup) | yes | The operation over the linked rows |\n| `field` | alias | no | The alias of the field on the linked table the operation reads; absent for `count` |\n\n<!-- generated:end field-rollup -->\n\nA rollup states `via` (a link of this table), `op` and \u2014 except for `count` \u2014 `field`, an alias on the\nlinked table; `op` is one of `count`,\n`sum`, `avg`, `median`, `min`, `max`, `range`, `empty`, `filled`,\n`percent_empty`, `percent_filled`, `unique`, `percent_unique`, `earliest`,\n`latest`, `date_range`, `checked`, `unchecked`, `percent_checked`,\n`percent_unchecked`. The operation must be one the aggregated field\'s type\nallows \u2014 `sum` over a number, `earliest` over a date, `filled` over any stored or\nformula field. A lookup is never rolled up: roll up the child\'s own field, or a formula\nover it. A rollup states no `notation`: `sum`, `avg`, `median`, `min`, `max` and `range`\nread in the aggregated figure\'s, a count as a plain number, and a `percent_*` as a\npercent (0\u2013100), in the app as in the table.\n\n### `lookup`\n\nDisplays a field from the linked records: `via` names a link of this table, `field` a field of the table it\npoints at, and `order_by` (`field`, `direction`) picks the one row whose value shows.\n\n<!-- generated:start field-lookup -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `via` | alias | yes | The alias of a link of this table to look up through |\n| `field` | alias | yes | The alias of the field on the linked table to show |\n| `order_by` | [Lookup order](#lookup-order) | no | Show ONE linked row\'s value \u2014 the first in this order \u2014 rather than every linked row\'s |\n\n#### Lookup order\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A field alias on the linked table the rows are ordered by |\n| `direction` | `"asc"` \\| `"desc"` | yes | Which end of that order the one row is taken from |\n\n<!-- generated:end field-lookup -->\n\nInside a formula, a lookup holding one value is that value (`{due_soon}` is\n`true`, not `[true]`); several values are a list.\n\n### `autonumber`\n\n<!-- generated:start field-autonumber -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `prefix` | text | no | Literal prefix prepended to every display value (e.g. \'KH-\' \u2192 \'KH-001\'). Ignored when `template` is set. |\n| `padding` | integer | no | Zero-pad the integer to this width. Default 1 (no padding). 3 \u2192 \'001\', \'012\', \'123\', \'1234\' (overflow uses the actual width). Ignored when `template` is set. |\n| `template` | text | no | Format template with placeholder tokens evaluated at insert time. Tokens: {N} (raw integer), {N:W} (zero-padded to width W, e.g. {N:3} \u2192 001), {YEAR} (4-digit year), {YEAR:2} (2-digit year), {MONTH} (2-digit month), {DAY} (2-digit day). Date tokens use the workspace timezone. Example: \'HM-{YEAR}-{N:3}\' yields \'HM-2026-001\'. Stored as the composed string; subsequent template edits do NOT re-format existing rows (date tokens would lose the original creation date). |\n| `next` | integer | no | Sets the number the next row takes, once, as the patch is written; never below one past the highest number issued. On a field added to a table with rows, those rows are numbered from it in creation order. It is no setting of the field: a read of the table shows none |\n\n<!-- generated:end field-autonumber -->\n\nA refused write and a dry run take no number, so a table\'s numbers run without a gap; a removed row\'s number is not\nissued again.\n\n<!-- generated:start rules-field -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `field.default` | A default names options its select declares \u2014 one on a single select \u2014 and a date stamped by `derive_from` states none. |\n| `field.default-from` | A field\'s `default_from` reads `<link>.<field>` off the row a one-row link of its table names \u2014 a link no copy fills \u2014 or, through further one-row links, `<link>.<link>.<field>` off the row the last names, into a field of the same type, date format and notation that `derive_from` does not stamp: a lookup only through a one-row link, a link only into one to the same table, a select\'s option as this field\'s of the same alias (one it lacks is not copied), several values only into a field holding several. The server copies it on every add that fills the link and leaves the field empty, so a List\'s `create` sure to fill the link \u2014 a required one, or the record it files the row under \u2014 does not ask it by default. |\n| `field.option-mark` | An option\'s `mark` is one the kit draws, and a select\'s options mark every one or none. |\n| `field.unit-select` | A notation\'s `per_row` names a single select of the same row, or a lookup of one through a one-link, whose every option label is a unit, or an ISO 4217 code. |\n| `field.link` | A link targets a table of the call or the workspace: its `pair` a link on that table naming this one back \u2014 and of two paired links at most one reads one row (`multi: false`). |\n| `field.formula` | A formula parses, reads fields of its own table by alias, names an option as `{field:option}` of one a select declares, and compares a select to its options\' keys, never their words. |\n| `field.formula-type` | A formula yields what it states \u2014 its `output_type` (the type the platform stores: a `datetime` is never stated `date`), else a figure where it states a `notation` and text where it states `link` \u2014 as the platform infers it from the expression when the field is created. |\n| `field.rollup` | A rollup aggregates, through a link of its table (`via`), a field of the linked table (`field`) by an operation (`op`) that field\'s type takes \u2014 never a figure read in each row\'s own unit or currency \u2014 filtered on fields of the linked table. |\n| `field.lookup` | A lookup reads (`field`), through a link of its table (`via`), a field of the linked table \u2014 a figure read in each row\'s own unit or currency only where this table looks up that row\'s select through the same one-row link \u2014 ordered by a field of it (`order_by`). |\n| `field.computed-cycle` | Computed fields never wait on each other in a loop: each is computed after what it reads. |\n\n<!-- generated:end rules-field -->\n\n## Views\n\nSaved views live under the table they belong to. Every field reference is a\nfield ALIAS on that table. A `set_tables` patch of an existing table refuses `views`: they are stated when the table is\ncreated, and change afterwards in chat.\n\n<!-- generated:start views -->\n\n#### View\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `alias` | alias | yes | Stable view alias, unique within the entity |\n| `label` | text | yes | Display name of the view |\n| `description` | text | no | The view\'s description, written onto the view in the workspace |\n| `columns` | list of [View column](#view-column) (at least one) | no | The columns the view shows, in this order and no others; absent, every field |\n| `filters` | [filter](#views) | no | The rows the view keeps |\n| `sort` | [sort](#views) | no | The order the view reads its rows in |\n| `summary` | map of text \u2192 text | no | Field alias \u2192 the operation its footer cell states |\n| `frozen_columns` | integer \\| `null` | no | How many leading columns stay in place while the rest scroll |\n\n#### View column\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field_alias` | alias | yes | A field of this entity |\n| `visibility` | `"visible"` \\| `"hidden"` | yes | Field visibility state: \'visible\' = shown to everyone, \'hidden\' = not shown by default but members can toggle |\n| `width` | number | no | The column\'s width, in pixels |\n\n<!-- generated:end views -->\n\nA filter is a group \u2014 `{ "node_type": "group", "logic": "and" | "or",\n"children": [ \u2026 ] }` \u2014 or one condition on its own, `{ "node_type":\n"condition", "type": "select", "field_key": "tier", "operator": "has_any_of",\n"value": ["gold"] }`. A sort is a list of `{ "field_key": \u2026, "order": "asc" |\n"desc" | null }`. A condition\'s `type` is the field\'s type and its `operator` is\none that type admits \u2014 `has_any_of` / `has_none_of` / `has_all_of` / `is_empty` /\n`is_not_empty` for a select, `equals` / `greater_than` / `less_than` for a\nnumber, `on` / `before` / `after` / `between` for a date, `contains` /\n`is_any_of` for text. A select condition\'s `value` names option ALIASES.\n\n<!-- generated:start rules-view -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `view.fields` | A view\'s columns, summary, sort and filters name fields of its table. |\n\n<!-- generated:end rules-view -->\n\n## Groups and templates\n\nA member group and a document template are made on their own, outside a table document and an app tree. A tree names a\ntemplate by its alias. A table document names a group by its id, where a `read_scope` clause (`member_of`),\na stage owner (`group`) or a check\'s `member_of("<id>")` takes one. `get_workspace` kind `group` lists the groups with their ids and members; `set_group` creates,\nchanges or deletes one (an admin\'s verb).\n\n`{{name}}` in an `html` or `email` template is filled from the workflow\'s data. Inline content is refused where it holds a\nscript, a frame, a `<link>`, an inline event handler or a `javascript:` URL; an `html` one may be a fragment, kept as\nwritten and printed as a whole document.\n\nA template\'s markup is the `template` of `set_template`, as one JSON string. A paper shaped like a counterparty\'s \u2014 a letterhead, a reference line, a seal, a signature block \u2014 is an `html` template with a shell around the body; `document_templates` covers the types.\n\n**What an act\'s template is handed** is its record\'s own fields by alias: a\nselect, a member and a link as their words (a link as the linked row\'s title), a\nnumber, a date and a computed value that declares its kind raw (`2800000`,\n`2026-09-14`) and a files field as its file cells (`{id, name, mime, url}`, the photos in the order held; a child row\'s\nfiles likewise) \u2014 never a computed select. On one record it is\nhanded, too, the rows of each child List the record draws, listed under that child\'s\nlink\'s name for `{{#each <link>}}` (and under the child entity\'s alias too): every row filed under the record, oldest first, each\nby its own fields as the record\'s are, less its link back to the record. Over\nseveral rows the rows are listed under the table\'s alias, for\n`{{#each <alias>}}`, without their child rows.\n\n**Photos in a paper.** A file cell is drawn only if whoever makes the paper may read that file, by the rule the file\nproxy applies; a file they may not read is never drawn. html: `{{#each photos}}<img src="{{url}}">{{/each}}`, each\nphoto inline, only those the data\'s file cells carry (a url typed into a text field stays a link). Word:\n`{{IMAGE picture(cover, 8)}}` for one file cell, and `{{FOR p IN photos}}{{IMAGE picture($p, 8)}}{{END-FOR p}}` for\nmany. Excel: `{{cover | picture:6}}` as the whole cell, and `{{#each photos}}` with `{{this | picture}}` for many, each\nrow growing to its picture. The number is the width in centimetres (8 by default, 6 in Excel); the height follows the\npicture. `picture` takes one image file cell: a list, a file that is no image and a file that cannot be read are\nrefused by name. Photos are embedded at most 1600 pixels across.\n\nHelpers print a raw value in the template\'s `locale` \u2014 `vi` or `en`, the organization\'s language where it states\nnone; each prints `""` for an empty value:\n\n| Helper | Prints (`vi` / `en`) |\n|---|---|\n| `{{money total "USD"}}` | `1.250,50 US$` / `$1,250.50`, in the currency\'s minor units; no code, a number |\n| `{{number qty}}` | `1.234,5` / `1,234.5`: grouped, up to 2 decimals |\n| `{{date issued_on}}` | `14/09/2026`; a date-fns pattern as the second argument, `{{date at "dd/MM/yyyy HH:mm"}}`, literal text quoted (`"\'Ng\xE0y\' dd"`). A moment prints on the workspace\'s timezone |\n| `{{words total "USD"}}` | `M\u1ED9t ngh\xECn hai tr\u0103m n\u0103m m\u01B0\u01A1i \u0111\xF4 la M\u1EF9 n\u0103m m\u01B0\u01A1i xu` / `One thousand two hundred fifty US dollars and fifty cents`; no code, VND (`\u0111\u1ED3ng`) |\n| `{{qr link}}`, `{{code128 position 6}}` | the value as a QR code, or as a Code 128 barcode (ASCII text only), a picture the width of the optional second argument in centimetres, by default sized to the value (a module prints 0.5 mm in a QR code, 0.33 mm in a barcode); a width too narrow to scan is refused, naming the least that does; not in an email template |\n| `{{date (add_days issued_on -1)}}` | `13/09/2026`: the date shifted by whole days (`-1`: the day before), for `date` to print; a number field as the days, empty shifting none |\n\nA value it cannot read (`{{money "abc"}}`, `{{date "14/09/2026"}}`) fails the render. A field named like a\nhelper and written alone (`{{number}}`) prints its own value.\n\nA field printed as a code in the other types: Excel `{{position | qr}}` or `{{position | code128:6}}` as the whole\ncell and the last helper \u2014 the picture is anchored on the cell and its row grows to hold it; Word `{{IMAGE qr(position)}}` or `{{IMAGE code128(position, 6)}}`. An empty field draws no picture.\n\n## Rows\n\n`rows` of a `set_tables` call are the first records of the tables the call creates, keyed by each one\'s `ref`;\na patch of an existing table seeds none, so write its rows into the table. A sample, not a data set: rows past it go in with `create_records` once the tables exist, in the batches its description bounds.\n\n```jsonc\n"rows": {\n "customer": [\n { "ref": "acme", "fields": { "name": "Acme Trading", "tier": "gold" } }\n ]\n}\n```\n\n`rows` sits beside `tables` in the call, not inside a table.\n\n<!-- generated:start rows -->\n\n#### Row\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `ref` | text | yes | Local handle for this row, referenced by other rows\' link fields |\n| `fields` | map of text \u2192 any value | yes | Field alias \u2192 the value, read against the field\'s declared type |\n\n<!-- generated:end rows -->\n\n<!-- generated:start rules-rows -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `rows.ref` | A row\'s `ref` is unique within its table. |\n| `rows.required` | A row states every field its table requires: it is created with what it states, its fields\' defaults and copies unapplied. |\n| `rows.value` | A row\'s value fits its field: an option alias for a select, `"<table ref>:<row ref>"` of a row of the linked table in this call for a link, `"self"` for a member, a `fil_` id for files, a date in the date grammar (an hour only on a datetime), a text, number, yes/no or null otherwise \u2014 and none on a computed field. |\n\n<!-- generated:end rules-rows -->\n\nA `ref` is lowercase letters, digits and underscores, and is never persisted.\n\n`fields` is keyed by field alias, and every value is read against the field\'s\nDECLARED type:\n\n| Field type | Value |\n|---|---|\n| `text` / `number` / `boolean` | the value itself |\n| `date` | `"2026-03-14"`, or a relative expression (below) |\n| `select` | the option ALIAS \u2014 `"gold"`, or `["gold","vip"]` for a multi-select |\n| `link` | `"<table ref>:<row ref>"` naming another row of this call \u2014 `"customer:acme"`, or an array for several; a paired link is stated on ONE side (the child\'s link to its parent) and its partner fills itself |\n| `member` | `"self"` only \u2014 the member making the call |\n| `files` | the `fil_` id of a file already in this workspace, or a list of them |\n| `formula`, `rollup`, `lookup`, `autonumber` | not allowed \u2014 the platform writes these |\n\n### Relative dates\n\nA date cell holds a literal `YYYY-MM-DD`, or an expression relative to the day\nthe call is made, so a screen that opens on "this month" is not empty a month\nlater:\n\n- `@today` \u2014 the day of the run, in the workspace\'s timezone\n- `@month-start` \u2014 the 1st of that month\n- either with a whole-day offset: `@today-14`, `@month-start+9`\n- on a date that holds its hour (`format: "datetime"`), that hour after the day, and always stated:\n `@today 14:30`, `@today+1 06:00`\n\n`@month-start` exists because `@today-N` cannot promise a month: applied on the\n2nd, `@today-3` lands in the previous one.\n\n## Records\n\n`records` says how a row of a table is RECOGNISED \u2014 stated once in the table\'s document, beside its\n`title` \u2014 and every surface that draws one of its rows reads the same\nstatement: the register\'s row, a picker\'s option, a link\'s chip, a child table\'s\nrow and the record\'s header. Every table an app lists, opens or picks has one. The rarer keys \u2014 `scan`, `cadence`, `place`, `on_site`, `pin`, `plan_place` and `size` \u2014 are stated on `model/record-places-pins-and-cadence`.\n\n```jsonc\n// table "visit"\n"title": "container_no",\n"records": {\n "subtitle": ["customer", "arrived_on"],\n "image": "photos",\n "status": { "field": "stage", "closed": ["gone"], "history": "visit_history" },\n "figure": "total_fees",\n "due": ["free_until"]\n}\n```\n\n```jsonc\n// table "release": a release order read against what it allows \u2014 a meter wherever it shows\n"title": "release_no",\n"records": { "figure": "issued", "limits": { "issued": "units" } }\n```\n\n```jsonc\n// table "dossier": a straight-line process \u2014 the stage a select, each step\'s day the date it stamps\n"title": "applicant",\n"records": { "status": { "field": "stage", "closed": ["signed"], "dates": { "received": "received_on", "appraised": "appraised_on", "signed": "signed_on" } } }\n```\n\n<!-- generated:start records -->\n\n#### Record\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `subtitle` | list of alias (1\u20132) | no | Up to two fields read under the title wherever a row is drawn (a code, a date, a party): one line at a desk, wrapping to a second on a phone |\n| `image` | alias | no | A files field that pictures the row \u2014 never one an act keeps the document it makes in |\n| `mark` | alias | no | A single select whose option leads the row wherever a mark leads it \u2014 a list\'s row, the record\'s head, a link naming the row: the option\'s brand or glyph, else its colour dot, on a tile. The field stays a field: a subtitle run, a column, a section\'s control. Never beside `image` or `party`, and never the status field |\n| `description` | alias | no | A text field saying what the row is about \u2014 a plan\'s brief, a customer\'s note: read in its record\'s head under the title\'s line, its first lines at rest and whole on a press, and written from the head\'s \u270E. Never on a row\'s line |\n| `party` | `"person"` \\| `"organization"` | no | What each row is where the rows are people (a contact, a patient) or organisations (a customer, a supplier, a carrier); absent, the rows are things (an order, an item, a paper) |\n| `status` | [Status](#status) | no | The stage a row moves through, a single select, drawn as a badge beside its title |\n| `figure` | alias | no | The one number the row stands for, read at its right (a total, a quantity left) |\n| `limits` | map of alias \u2192 (alias \\| number) | no | A number read against a bound \u2014 a field of the same row or a constant \u2014 drawn as a meter wherever it shows |\n| `gates` | map of alias \u2192 (alias \\| number) | no | A pass mark on a meter `limits` bounds \u2014 a field of the same row or a constant; a percent field is its share of the bound, any other number an amount. The meter marks it and fills complete once past it |\n| `tolerance` | map of alias \u2192 number | no | How far past its `limits` bound a meter may stand, in percent of the bound: the bound is an amount expected (received against ordered), not a cap. Without one the bound is a cap, and past it is an overrun |\n| `due` | list of alias (at least one) | no | Dates that are deadlines (stored, or computed as a date): each counts down and turns overdue |\n| `frees` | alias | no | A date ending the row\'s span of days on which what the row holds is free again (a check-out, a hire\'s return): a stay from the 3rd to the 5th holds the 3rd and the 4th, and another may start on the 5th \u2014 on a lanes board, a calendar, a roster and in `no_overlap` alike. Absent, a span of days holds its last day; a span of moments always frees its end |\n| `settled` | alias | no | A yes/no of the row (stored, a formula: an invoice paid in full, or looked up through a one-row link: a line settled with its parent) that ends its deadlines: while it is true, no `due` of the row counts down or turns overdue \u2014 in a cell, on its line, in its head, on a calendar or gantt, or in the overdue count. A dashboard\'s or reading\'s `where` reads it as any other yes/no |\n| `task` | `true` | no | A row is work someone finishes: wherever rows of it stand in a table \u2014 a register, a record\'s rows, the rows filed under one \u2014 each is led by a ring ticking it done and unticking it by the one write of the app making that move. Its status is a stored select with `closed` |\n| `applies` | map of alias \u2192 map of alias \u2192 (list of alias (at least one) \\| boolean) | no | Fields that apply only while their conditions hold (a length only on a line whose tariff charges by the metre): where one does not apply no add asks it and no surface shows it. Never the title; a required one starts at its `default` |\n| `starts` | map of alias \u2192 `"today"` \\| `"me"` | no | What a field of a row being added starts at, where that start is certain: `today` a date (a moment starts now), `me` a member, the reader adding it; every other field starts empty unless the reader narrowed the register to a value or the field declares a `default` |\n\n<!-- generated:end records -->\n\n<!-- generated:start rules-records -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `records.title` | A title is the one field naming the row \u2014 any field but a button or a yes/no \u2014 and the subtitle never names the title\'s own field. |\n| `records.image` | `image` is a files field, never one an Act keeps the documents it makes in (`into`). |\n| `records.mark` | `mark` is a single select, never the status field, and never beside `image` or `party` \u2014 a List keeps one lead slot \u2014 nor on the folder an App works inside (`scope`), which its switcher names by a picture or its initials. |\n| `records.description` | `description` is a text field, read in the record\'s head \u2014 never a Field of a part too. |\n| `records.figure` | A figure is a number. |\n| `records.figure-on-line` | *Noted, never refused:* A subtitle entry equal to the figure is shown once, as the headline; the line leaves it out. |\n| `records.applies` | `applies` holds a field of the row other than its title to conditions of the same row \u2014 a yes/no (stored or a formula) by `true` or `false`, a single select by its options, its own or looked up through a one-row link \u2014 never to itself; a required field it holds starts at a `default`. |\n| `records.limits` | `limits` bounds a number by a number field of the row, in its unit or one of the same dimension \u2014 or by a constant, where every row reads the figure in one unit. |\n| `records.gates` | `gates` marks a pass on a meter `limits` bounds: a percent field (its share of the bound), another number in the bound\'s unit, or a constant. |\n| `records.tolerance` | `tolerance` stands on a meter `limits` bounds. |\n| `records.starts` | `starts` starts a field a person writes \u2014 `today` a date or a moment (never a span), `me` a member \u2014 and a `create` that does not ask a field it starts writes that start. |\n| `records.due` | A deadline (`due`) is a date. |\n| `records.frees` | `frees` names a stored day, never a moment: a span of moments already frees its end. |\n| `records.settled` | `settled` names a yes/no of the row (stored, a formula, or looked up through a one-row link) beside a `due`: while it holds, the row\'s deadlines neither count down nor turn overdue. |\n| `records.task` | A task entity (`task`) states a `status` of a stored single select with `closed` options \u2014 its ring ticks a row done by moving it to one. |\n| `records.picture` | *Noted by the designer on a write, never refused:* Rows an App draws that each hold one files field no Act makes documents into, no call is filed into, no files gallery keeps and no List compares as a pair (`compare`) \u2014 and no figure \u2014 are pictured by it (`image`); where the workspace\'s files are known, the field holding images is the picture, a figure or not. |\n\n<!-- generated:end rules-records -->\n\n- **The title names the row** by who or what it is \u2014 a link by the linked row (a job\n read by its customer). Any field but a button or a yes/no may title it, yet a code\n titles a row only where people name the row by it, else it is a `subtitle`; it is the table\'s own setting, which every link shows.\n- **One lead per row.** A row leads with its picture (`image`), a face (`party`) or the\n option of a single select it holds (`mark`) \u2014 never two. A `mark` draws that option\n on a tile: its brand or glyph, else its colour dot; a row holding none, a ring saying\n none is picked. The same tile heads the row\'s record and leads it where a link\n names it; the select stays a field \u2014 a subtitle run, a column, a section\'s control.\n- **A limit** reads a number against a bound \u2014 another field of the row, or a constant.\n A `gates` entry marks a pass on that meter: a percent field is its share of the\n bound, any other number an amount.\n- **A deadline** (`due`) counts down wherever it is drawn and is overdue once past while\n the work is open. A register over the table says how many rows in view are overdue on\n its summary line, and pressing that count narrows to them \u2014 no reading to state.\n- **A span\'s free day** (`frees`) is the end date on which what the row held is free\n again \u2014 a check-out, a hire\'s return. A stay from the 3rd to the 5th holds two nights:\n a lanes board, a calendar and a roster draw it through the 4th, and `no_overlap` lets\n the next stay start on the 5th. Without it a span of days holds its last day; a span of\n moments always ends as the next may begin.\n- **`starts`** is where an add\'s field begins, stated only where that is certain \u2014 a\n log\'s own day at `today`, a request\'s requester at `me`. Nothing else starts a field\n but its declared `default` and what the reader narrowed the register to.\n\n## Record places, pins and cadence\n\nThe rarer keys of `records`, stated beside the ones on `model/records`: a title read off a photo (`scan`), the rows a child owes each day (`cadence`), where a row stands on a map (`place`, `on_site`) or on a picture (`pin`, `plan_place`).\n\n```jsonc\n// table "site": it owes a diary page each day it works, from breaking ground through handover\n"title": "name",\n"records": { "cadence": [{ "entity": "diary_page", "on": ["mon", "tue", "wed", "thu", "fri", "sat"], "from": "started", "until": "handed_over" }] }\n```\n\n<!-- generated:start records-places -->\n\n#### Record places\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `cadence` | list of [Cadence](#cadence) (at least one) | no | The rows of a child each row owes one of a day, on the days of the week listed (a site\'s diary page, Monday to Saturday) \u2014 one entry a child, never owed on a day still to come. A roster of these rows over days marks a past day owed with none missing in the danger tone, counted in the legend; today\'s is due, offered to add and never marked; each row\'s total reads the days it holds of those owed |\n| `scan` | `"text"` \\| `"iso6346"` \\| `"code"` | no | Its title, a text field, is painted or printed on the thing a row is (a container\'s number, a seal\'s) and read off one photo on a phone, beside each search matching it and each field holding or picking it: `iso6346` a container number, refused unless its check digit holds; `text` as it stands; `code` a barcode or QR code on the thing, decoded on the device from the photo with no agent |\n| `place` | [Place](#place) | no | Where a row stands, as plain fields of it, drawn as one place where its record draws the address: how sure it is, a press opening it in the device\'s maps, and one taking it from where the device stands |\n| `plan_place` | [Plan place](#plan-place) | no | Each row is a place on the plan a `plan` register draws: its title the place\'s code, its subtitle a note. A flat list, no tree |\n| `on_site` | [On site](#on-site) | no | Photos taken where a place stands (a site diary\'s): a photo taken through a phone\'s camera here keeps where the device stood, marked at the place, away from it or unknown, and a record holding one taken away says so among its checks \u2014 one with no place (picked on a computer, or kept before places were) is marked and warns nothing; a place reaches only members of the organization, through the apps drawing these photos |\n| `pin` | [Pin](#pin) | no | Each row is a place on a picture (a snag on a flat\'s plan, a spot on a container\'s drawing): a List of these rows draws the picture its `on` row holds with a numbered pin at each row\'s own place, each row led by its number, the row opened lit on it and a pin pressed opening its row; where the List writes rows, a press on the bare picture adds one there, and a pin dragged moves its row. A List of rows that link to these by a single link draws their places the same way |\n| `size` | [Record size](#record-size) | no | The real size of the thing a row is (a carton, a piece of furniture, a pallet): three number fields stating one length unit alike, read as one fact \u2014 "400 \xD7 300 \xD7 250 mm", a part the row lacks "\u2014" \u2014 after the subtitle wherever the row\'s line and head draw facts, never also as the subtitle\'s fields. A plan register drawing these rows draws each box at this size |\n\n#### Cadence\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `entity` | text | yes | The table owed: each of its rows stands on one row of this table by its single link to it, and on the first date of its line (`records` title or subtitle) |\n| `on` | list of `"mon"` \\| `"tue"` \\| `"wed"` \\| `"thu"` \\| `"fri"` \\| `"sat"` \\| `"sun"` (at least one) | yes | The days of the week one is owed on, each listed \u2014 `mon` to `sun` |\n| `from` | alias | no | A date of this row: the first day one is owed (a site breaking ground, a contract\'s start); absent, no first day |\n| `until` | alias | no | A date of this row: the last day one is owed (a site\'s handover, a contract\'s end); empty or absent, through today |\n\n#### Place\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `address` | alias | yes | A text field: the address, as people write it |\n| `latitude` | alias | yes | A number field: degrees north, negative south |\n| `longitude` | alias | yes | A number field: degrees east, negative west |\n| `accuracy` | alias | no | A number field: how many metres off the device that gave the place said it may be |\n\n#### On site\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `photos` | list of alias (at least one) | yes | Files fields of this row holding photos taken on site |\n| `place` | alias | no | A single link of this row to the row whose `place` its photos are taken at (a diary page\'s site); absent, this row\'s own `place` |\n| `radius` | number | yes | How many metres from the place a photo still counts as taken there |\n\n#### Pin\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `on` | alias | yes | A single link of this row to the row whose picture (its `records` `image`) the place is on \u2014 a container\'s drawing, a floor\'s plan |\n| `x` | alias | yes | A number field of this row: how far across that picture the place stands, in percent from its left edge |\n| `y` | alias | yes | A number field of this row: how far down that picture the place stands, in percent from its top edge |\n\n#### Record size\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `across` | alias | yes | A number field: the width of the thing a row is, across |\n| `along` | alias | yes | A number field: its depth, along |\n| `up` | alias | yes | A number field: its height, up |\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `records.scan` | A title read off a photo or a code (`scan`) is a text field the row stores. |\n| `records.cadence` | A `cadence` names each child once \u2014 its rows standing on one row of this entity by their single link and on the first date of their line \u2014 lists each day of the week once, and `from` and `until` are two different days of this entity. |\n| `records.place` | A `place` names four different fields of the row: its address a text field, its latitude, longitude and accuracy number fields it stores. |\n| `records.plan_place` | A `plan_place` names `kind` and `reach_from` as single selects, `sheet` as a single select or a one-row link, `stacked` as a boolean and the rest as numbers, each a different field; `kind`, `x`, `y` required. `size` names three numbers (`across`, `along`, `up`) stating one cell in one length `unit` alike. A `sheet` linking to an entity that states an `image` draws that drawing under the places, `scale` (a number field of that entity) its width in cells; `scale` goes with such a `sheet` alone. |\n| `records.on_site` | `on_site` lists files fields of the row, each once, taken at the `place` of the row itself or of the one row a single link of it names (`place`), which states one. |\n| `records.pin` | A `pin` names a single link of the row (`on`) to an entity whose `records` states its `image` (a PDF is drawn as its first page), and two different number fields of the row, how far across (`x`) and down (`y`) that picture in percent. A List of rows whose own entity states a `pin` is drawn over that picture; `on`, `x` and `y` are set by the press that adds a row, never asked. |\n| `records.size` | A `size` names three different number fields of the row (`across`, `along`, `up`) stating one length unit alike (mm, cm, m or km); they read as one fact after the subtitle, so the subtitle never also names one. |\n\n<!-- generated:end records-places -->\n\n## Status\n\nA record\'s `status` is the stage a row moves through \u2014 a stored single select, each\noption\'s moment the date `dates` names \u2014 drawn as a badge beside its title wherever the row is.\n\n<!-- generated:start status -->\n\n#### Status\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The single select that is the row\'s state, its options in the order the work goes through them |\n| `closed` | list of alias (at least one) | no | Options that end the work: a register opens on the rows not at them, and the badge reads muted |\n| `dropped` | list of alias (at least one) | no | Options of `closed` that leave the work undone (cancelled, rejected): whoever holds the row moves it to one from any stage, where every other move forward passes each owned stage between that binds the row (its `when`) and needs each one\'s owner, and a row leaving one enters a stage as a row made there does |\n| `history` | text | no | A table every write that sets or moves the status appends one row to (its link to this table, the new status, the moment and who) \u2014 by the record write path, whoever writes: for reporting on every move |\n| `dates` | map of alias \u2192 alias | no | Option alias \u2192 a date field of this entity the record write path stamps with the moment a row moves into that option, or is made at it \u2014 whoever writes. A value the same write states for that field wins, so past data loads as it happened; the platform never clears a stamp, so each is when the row last entered the option. A stage\'s date is made where none is named |\n| `stages` | map of alias \u2192 [Stage](#stage) | no | Option alias \u2192 who owns that stage and how long it should take: the owner alone takes a row out of it, by any write, and presses the acts offered there; every move forward past it of a row it binds (its `when`), and every such row made past it, is its owner\'s too. Its days run from the moment its `dates` field holds. In an app the status then moves only by acts that `set` one of its options \u2014 no save writes it and no act asks it |\n\n#### Stage\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `owner` | text \\| [Stage group](#stage-group) | yes | Who owns the stage: a member field of the row, `link.field` read through a one-row link (not at the stage a row opens at, whose owner made the row: a member field of the row itself), or `{ group }` |\n| `within` | integer | no | Whole days the stage should take from the moment the row entered it |\n| `when` | map of alias \u2192 (list of alias (at least one) \\| boolean \\| [comparison](#values) \\| `{ "empty": true \\| false }`) | no | The rows that must pass this stage: a row meeting every entry on its own fields, as it stood before a write or as the write leaves it \u2014 a single select holding one of these options, a yes/no this value, a number or date meeting its bounds, a field empty or filled. Absent, every row must. A row at the stage leaves it by its owner either way |\n\n#### Stage group\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `group` | text | yes | A member group\'s id (from Settings, or chat): every member of it owns the stage |\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `records.status` | A status is one single select, its options in the order the work goes through them, `closed` naming the ones that end it and `dropped` those of them reached from any stage. |\n| `records.history` | A declared status `history` has exactly one link to the row it records, one single select holding every option of the status (or a text), exactly one date and at most one member. |\n| `records.stages` | A stage\'s owner (`status.stages`) is a single member field of the row, one read through a one-row link (never at the stage a row opens at: its owner made the row, so it is a member field of the row itself), or a group, on an option that does not end the work; a group\'s stage needs the status `history` with who moved the row. Every write takes a row off a stage as its owner alone, and moves a row past it or makes a row past it as its owner alone where the stage binds the row (its `when`) \u2014 a move to a `dropped` option excepted, which whoever holds the row makes from any stage; a row leaving a dropped option, or holding none, enters a stage as one made there. Each stage\'s moment, and each open option\'s, is the date its `dates` names, made where none is named, which every write that opens or moves the status stamps; `within` runs from it. In an App a status with stages moves only by Acts that `set` one of its options \u2014 no save writes it or a date it stamps, and no Act asks either, sets it otherwise or writes either from a workflow; an Act moving a row forward past a stage another owner owns, into an option not `dropped`, keeps its `when` off every row that stage binds. |\n| `records.stage-when` | A stage\'s `when` names the rows that must pass it, as an Act\'s `when` holds them \u2014 single selects to their options, yes/nos to `true` or `false`, numbers and dates to bounds, any field to `{ "empty": true \\| false }` \u2014 on fields a write stores of the row itself: never the status, a formula, a lookup or a rollup, and never on the stage a row opens at. It binds a row meeting it as it stood before a write or as the write leaves it; absent, it binds every row. |\n| `records.detour-note` | *Noted, never refused:* A stage the work only steps aside into \u2014 not where a row starts, entered from one step alone that also moves the work on without it, and left by its own moves only back to that step (a pause, a repair) \u2014 is a detour: its option and the Section whose `at` holds it stand before that step\'s, else the work reads it as its next step, and the Act moving the work into it leads that Section \u2014 never a correction waiting in a \u22EF. |\n\n<!-- generated:end status -->\n\n- **The status** is one single select. `closed` options end the work: a register opens\n on the rows not in them. A `history` table gets one row per move \u2014 its link to this\n table, the new status (a select holding the same option aliases, or text), the\n moment and who \u2014 appended by the record write path in every write that opens a row at a\n stage or moves it, whoever writes (the member behind the write is its `by`; an automation\n leaves it empty). A `history` the tables do not declare is derived, and needs no\n `records` of its own.\n- **When each step happened** is the date `dates` names for its option, stamped as the row\n enters it, whoever moves it. One way times a status: a straight-line process is a select\n whose options are its steps in order, each with its date \u2014 never a run of dates the stage\n is read off. An Act that moves the stage and `asks` the step\'s date with `starts: "today"`\n lets the reader record a day already past in the same press.\n\n## Write rules\n\nWhat a WRITE of a table\'s rows meets is stated in the table\'s document, where it applies: `min` and\n`max` on a number or date field, `options_where` and `same` on a link, `suggest` on a text field,\nand `no_overlap` and `natural_key` on the table. A key a field\'s type does not take is refused at\nits path, and a document has no `write_rules` key.\nEvery one but `natural_key` and `suggest` holds on every path a record is written by \u2014 the grid, a record\'s form, an import, a workflow, an\nagent, the API, an app \u2014 judged in the write\'s own transaction, every broken rule listed at once; the\nscreen and a picker only mirror them. A row that already breaks one stays editable until a write\ntouches what the rule reads. `natural_key` names the fields an app recognises a row by, to reuse it where an\nadd or a find-or-create meets it; a record tool or the API creates the row it is handed. `suggest` offers\nvalues in a text control and refuses none. An `options_where` link is refused where a write names a row the\nnarrowing does not admit, and every picker of the link offers only the rows it admits.\n\n```json\n{\n "tables": [\n {\n "ref": "customer",\n "label": "Customer",\n "fields": { "email": { "type": "text", "label": "Email", "unique": true } },\n "natural_key": ["email"]\n }\n ]\n}\n```\n\nA customer above is RECOGNISED by their address. An add that names one \u2014 from the customers\nregister or from a picker\'s "new" \u2014 reuses the row it matches and opens one only where nothing does,\nso the book never grows a second Acme.\n\n```json\n{\n "tables": [\n { "ref": "product", "label": "Product", "fields": { "name": { "type": "text", "label": "Name" }, "in_stock": { "type": "number", "label": "In stock" } } },\n {\n "ref": "order",\n "label": "Order",\n "fields": {\n "name": { "type": "text", "label": "Name" },\n "placed_on": { "type": "date", "label": "Placed on" },\n "due_on": { "type": "date", "label": "Due on" },\n "lines": { "type": "link", "label": "Lines", "table": "order_line", "multi": true, "pair": "order" }\n }\n },\n {\n "ref": "order_line",\n "label": "Order line",\n "fields": {\n "name": { "type": "text", "label": "Name" },\n "order": { "type": "link", "label": "Order", "table": "order", "multi": false, "pair": "lines" },\n "quantity": { "type": "number", "label": "Quantity", "min": 1, "max": 9999 },\n "ships_on": { "type": "date", "label": "Ships on", "min": "order.placed_on", "max": "order.due_on" },\n "product": {\n "type": "link",\n "label": "Product",\n "table": "product",\n "multi": false,\n "options_where": {\n "node_type": "group",\n "logic": "and",\n "children": [{ "node_type": "condition", "type": "number", "field_key": "in_stock", "operator": "greater_than", "value": 0 }]\n }\n }\n }\n }\n ]\n}\n```\n\nA line of nothing is not a line: `quantity` is refused on create and on update. A line ships inside\nits order\'s window \u2014 a bound read off the row a link names is judged when the line is written. Nothing is sold off an empty shelf: every picker of `product`\nreads only the rows that answer its `options_where`, and every save refuses the same rows again.\n\n```json\n{\n "tables": [\n {\n "ref": "booking",\n "label": "Booking",\n "fields": {\n "room": { "type": "text", "label": "Room" },\n "state": { "type": "select", "label": "State", "options": [{ "alias": "held", "label": "Held", "color": "blue" }, { "alias": "released", "label": "Released", "color": "gray" }] },\n "starts_on": { "type": "date", "label": "Starts on" },\n "ends_on": { "type": "date", "label": "Ends on", "min": "starts_on" }\n },\n "no_overlap": { "from": "starts_on", "to": "ends_on", "by": ["room"], "while": { "state": ["held"] } }\n }\n ]\n}\n```\n\nOne booking per room at a time, while it is held \u2014 a create or a save whose span overlaps another\nheld booking of the same room is refused.\n\nA table\'s `checks` (the conditions no write of its rows may meet) and `workflows` (what follows every committed write of them) are stated in its document and hold on every path a record is written by; their keys, what they read and an example are on `workflows/table-checks` and `workflows/table-workflows`.\n\n<!-- generated:start write-rules -->\n\n#### Suggest\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `table` | text | yes | The table whose text field is offered from: its id, or its `ref` in this call |\n| `field` | alias | yes | The alias of that table\'s stored text field; the field\'s control offers, as the reader types, the distinct values it holds, and any text is still written |\n\n#### No overlap\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `from` | text | yes | The stored date (or datetime) a row\'s span starts on: `<field alias>` of the row, or `<link alias>.<field alias>` of the one row a one-row link names (a booking line\'s piece held for its booking\'s dates) |\n| `to` | text | yes | The stored date (or datetime) a row\'s span ends on, named like `from` and through the same link \u2014 a day it holds, unless `records` names it the day the row `frees`; a moment another row may start at |\n| `by` | list of alias (at least one) | no | Fields two rows share to compete for a span (the same employee, the same room); absent, every row |\n| `while` | map of text \u2192 list of alias (at least one) | no | Only rows at these options of these selects hold their span (running, not closed), the select named like `from` \u2014 `<field alias>` or `<link alias>.<field alias>`; absent, every row |\n\n<!-- generated:end write-rules -->\n\n<!-- generated:start rules-write -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `write.natural-key` | A `natural_key` is text or a number a person types back, or a one-row link; a text key is unique \u2014 `unique: true`, or the key\'s fields as a set in the table\'s `unique`. |\n| `write.bounds` | `min` and `max` bound a number or a date \u2014 by a constant a number, by a field of this row or of the one row a one-row link names either, of the same type and unit, never by itself \u2014 and `min` is never above `max`. |\n| `write.bound-per-row-unit` | On a figure whose unit or currency is the row\'s own (its notation\'s `per_row`) a constant bound is only 0 \u2014 bound it by a field of the row. |\n| `write.suggest` | `suggest` stands on a stored text field and names a `table` and a `field`, a stored text field of that table; the app reads that table, so it has a `records` entry. |\n| `write.same` | `same` stands on a link and names one-row links this table and the link\'s target both hold to one table. |\n| `write.options-where` | `options_where` narrows a link by an `and` group of plain conditions over the target\'s own fields \u2014 a number compared in one unit on every row, a select by options it declares. |\n| `write.no-overlap` | `no_overlap` spans two date fields, the row\'s own or the one row a one-row link names (`booking.start_on`); `by` names values rows share \u2014 a link, a person, one option, a text or a number; `while` names single selects by their options, the row\'s own or that linked row\'s. |\n\n<!-- generated:end rules-write -->\n\n- A `natural_key` is `text` or `number`, because a person types it back, or a\n one-row link, because a row is also recognised by the one row it belongs with (a\n fee by its visit and its kind). A key holding a `text` field is unique: `unique:\n true` on that field, or the key\'s fields as a set in the table\'s `unique` (a\n site by its customer and its name) \u2014 two rows sharing it would make\n find-or-create pick whichever the read answered first. An add asks the key by\n default.\n- A `same` link names only rows that name what this row names \u2014 a fee\'s invoice\n is one of its own visit\'s \u2014 through a one-row link both tables hold to one\n table; its picker offers nothing until the row names one. A row added under a\n record names what it copies off it (`default_from`) from the start, so a block\n expecting one line per row of the link lists that record\'s rows alone. A save\n of this row moving what the two compare is refused while they would name apart.\n- An `options_where` link may be left empty; a row it names is refused on every\n save that writes the link unless it holds the narrowing, and the refusal names\n the link and the row. A save that leaves the link alone is not judged, so a row\n still naming a row the narrowing no longer admits stays editable. A link naming\n one kind of a book\'s rows (a "Shipping line" among the parties) narrows to that\n kind, or its picker offers every row.\n\nA field\'s own `required` is not here: the field carries it, and every write\npath refuses the row by field name from it. `unique` is the text field\'s own\nclause (\xA7 `text`) \u2014 an add says so at the control before the column does. A\nfield the workspace writes (a formula, a rollup, a lookup, an autonumber, a date\nwith `derive_from`) is never asked, written or edited.\n\n## App tree\n\nAn app is a tree of components, assembled the way a page is written in JSX: pick a component, bind it to a table by\nname, and it draws well with no props, because its defaults come from the table\'s facts (\xA7 Records, \xA7 Status,\n\xA7 Write rules, \xA7 Field). A prop overrides a default at its own node. No prop says how anything looks: the runtime\ndraws each concept one way, and a screen the job needs that no component states is told to the person \u2014 the job,\nwhat the app drew, the word wanted \u2014 never props bent to approximate it.\n\nEach component is a page of its own: `model/app-node`, `model/list-node`, `model/record-node`, `model/section-node`,\n`model/side-node`, `model/page-node`, `model/outside-node`, `model/tab-node`, `model/field-node`, `model/act-node`, `model/check-node`,\n`model/reading-node`, `model/progress-node`, `model/statement-node`, `model/export-node`, `model/book-node`,\n`model/query-node` and `model/workflow-node`. Beside them: `model/changing-an-app` and `model/what-an-app-may-write`, `model/layouts`, `model/time-and-place-layouts` and\n`model/publish-desk` for a List\'s `layout`, `model/composed-page`, `model/tasks`, and the rules, `model/app-rules`,\n`model/reading-rules`, `model/statement-rules`, `model/check-rules`, `model/register-rules`, `model/block-rules`, `model/record-rules` and `model/act-rules`.\n\n<!-- generated:start app-components -->\n\n| Component | Draws | Holds |\n|---|---|---|\n| [App](#app-node) | the launcher tile, the app\'s words, its Lists, and its API | List, Tab, Reading, Query, Workflow |\n| [List](#list-node) | rows of an entity: the register, a record\'s child rows, a roster\'s cells, related rows, a log | Tab, Reading, Progress, Export, Act, Check, Book, Record, List |\n| [Record](#record-node) | what a row opens: the head and the parts | Field, Section, Side, Page, Outside, List, Reading, Progress, Statement, Act |\n| [Section](#section-node) | a titled part of a record, and optionally a step of its path | Field, List, Reading, Progress, Statement, Act |\n| [Side](#side-node) | the part the rest is worked from, beside them | Field, List, Reading, Progress, Statement, Act |\n| [Page](#page-node) | a part on its own page, off the record\'s scroll | Field, List, Reading, Progress, Statement, Act |\n| [Outside](#outside-node) | what one record shows, and which acts it allows, to a person with no seat who opens a link a member made of it | Field, List, Act |\n| [Tab](#tab-node) | one view of a List\'s rows over a period, or one sheet of a dashboard | Reading, List |\n| [Field](#field-node) | one field: a control, a gallery, prose, a link and its picker, the rows filed under a back-link | \u2014 |\n| [Act](#act-node) | a verb: what pressing it does | \u2014 |\n| [Check](#check-node) | a formula of the rows that warns, or refuses the writes and acts it names | \u2014 |\n| [Reading](#reading-node) | a number over rows: one figure, its parts, a grid, per period, steps; or a record\'s own figure | \u2014 |\n| [Progress](#progress-node) | the work against its plan, day by day | \u2014 |\n| [Statement](#statement-node) | a figure built from figures of the record | \u2014 |\n| [Export](#export-node) | a report file of the rows in view | \u2014 |\n| [Book](#book-node) | another table read as part of the register | Record, Act, Check |\n| [Query](#query-node) | nothing: a read for agents | \u2014 |\n| [Workflow](#workflow-node) | nothing: a verb for agents | \u2014 |\n\n<!-- generated:end app-components -->\n\n### A node\n\nA node is `{ "type": <Component>, "key"?, ...props, "children"?: [...] }`. `type` names the component, `key` its\nname among its siblings, the other keys its props, `children` the nodes it holds. An unknown prop is refused.\n\n- **Two shorthands, both for Field.** A bare string child is a Field (`"due_date"`); an object with `field` and no\n `type` is a Field with props (`{ "field": "price", "edit": false }`). Both work in every prop that lists fields:\n `create`, `row.subtitle`, `add`, and the `children` of a Record or a part. A column is a field\'s alias, or\n `{ "field", "edit": true }` for the line\'s own control, and takes no other prop.\n- **Fields and options are named by alias**, never by label. A tagged value is an object, never a magic string.\n- **`false` turns a default off at its node:** `"figure": false` drops the row\'s figure, `"edit": false` makes a\n field read-only, `"create": false` and `"remove": false` take a List\'s add and Delete away, `"readings": false` its\n band, `"row": { "status": { "edit": false } }` the status buttons (another app moves the stage) and\n `"row": { "image": false }` the picture.\n\n### Keys and addresses\n\nA key is unique among its siblings, the way a React key is. A node that binds data takes its key from what it binds:\n\n| Node | Key when none is stated |\n|---|---|\n| Field, Check | its field alias |\n| List, Book | its entity alias |\n| Record | `record` |\n\nEvery other node states its key. Three kinds of key are unique across the whole app, because each is an API name:\nan Act\'s (its workflow is `act_<key>`, or the one it names), a Reading\'s (its query name for `run_app_query`), and a Query\'s or\nWorkflow\'s.\n\nA node\'s **address** is the path of keys from the app, such as `customer/record/orders`; the app itself is `/` (the `in` of a node at the root), and its own key is part of no address. A patch may use any ending\nof that path that names exactly one node (`orders`). The exact path wins where one exists; an ending naming two\nnodes is refused, and the refusal lists both paths. `get_app` prints the tree with each node\'s address.\n\n### Values\n\n| Value | Written |\n|---|---|\n| now, the reader, an asked input | `{ "now": true }`, `{ "me": true }`, `{ "input": "name" }` |\n| a computed value | `{ "formula": "{cost} * 1.2" }` over the acted row\'s fields |\n| a call\'s own value, in a Query | `{ "param": "name" }` |\n| cleared | `null` in an Act\'s `set` |\n| an option | its alias |\n\nA **condition** (`when`) maps a select to a list of its option aliases and a yes/no to `true` or `false`; every entry\nholds. A number (a number, currency or percent field, or a formula or rollup yielding one) takes a comparison with any\nof `gt`, `gte`, `lt`, `lte` (at least one, never both `gt` and `gte` or both `lt` and `lte`), each a number; a date\ntakes the same keys with a day (`"2026-03-01"`), a moment on a datetime (`"2026-03-01T09:30"`) or `{ "now": true }`\n\u2014 today, or the current minute on a datetime, read when the rows are, so overdue is `{ "due_on": { "lt": { "now": true } } }`;\nand any field but a yes/no takes `{ "empty": true }` or `{ "empty": false }`. A comparison on a field it does not fit is\nrefused, naming the field, its type and what it takes. A `when` on an Act, a Section or other block, and a List\'s `where`\n(the register\'s or a child List\'s) take these; a child List that adds, picks or expects rows refuses a comparison in its\n`where`, since a row it holds could stand outside it. A **narrowing** (`opens`, a Reading\'s, a Tab\'s or an Export\'s `where`) takes options, yes/nos and also `{ "me": true }` on a member\nfield, the reader; `where` on the register, a log or a record\'s child rows fixes the rows on the server for every reader, so it refuses\n`{ "me": true }` \u2014 open on it with `opens`; on a record\'s related rows (`same`, `app`) and on a dashboard\'s List it\nnarrows by the reader and takes it, and those two take no comparison.\n\n## Changing an app\n\n1. `get_workspace` with `kind: "app"` names the app. `get_app` prints the `tree:`, the `node addresses:` (the address of\n each node the app draws, `(default)` on those the tree leaves to the platform) and the `version` line, whose id is the `base_version_id` to send. A member who is not the\n app\'s author is shown no tree. Keep the tree: no tool reads an earlier one.\n2. `set_app` with `app_id`, `base_version_id` (that version: a newer write is refused, not overwritten) and `nodes`,\n (a refused change writes nothing, so `dry_run` is for reading the permissions it would grant and remove). Every answer\n states the permissions the change grants and removes.\n3. `screenshot_app` shows the result.\n\n`nodes` maps an address to a patch of that node. Read against the customers List of the account review (\xA7 A complete\nworkspace, abridged), the call below makes `tier` read-only, moves the Orders Section ahead of Account, adds an Act to\nAccount and removes the Reading:\n\n```json\n{\n "type": "List",\n "entity": "customer",\n "children": [\n { "type": "Reading", "key": "ordered_by_tier", "by": "tier", "value": "total_ordered" },\n {\n "type": "Record",\n "children": [\n { "type": "Section", "key": "account", "title": "Account", "children": ["tier"] },\n { "type": "Section", "key": "orders", "title": "Orders", "children": [{ "type": "List", "entity": "order" }] }\n ]\n }\n ]\n}\n```\n\n```json\n{\n "app_id": "<id of the app>",\n "base_version_id": "<current_version_id>",\n "nodes": {\n "tier": { "edit": false },\n "orders": { "before": "account" },\n "mark_gold": { "type": "Act", "in": "account", "label": "Make gold", "when": { "tier": ["standard"] }, "set": { "tier": "gold" } },\n "ordered_by_tier": null\n }\n}\n```\n\nEach entry applies in order:\n\n- **An existing node:** a JSON merge patch (RFC 7396) of its props; `null` drops a prop, and a list (`children`,\n `columns`) replaces the node\'s whole.\n- **`null`:** removes the node and everything under it.\n- **An address naming no node, with `type` and `in`:** adds a node there; its key is the address\'s last word, and a\n Field states `field` in place of `type`.\n- **`in`, `before`, `after`:** move a node into another, or ahead of or behind a sibling.\n\nThe app\'s own props (`name`, `description`, `icon`, `color`, `words`, `scope`, `period`) change\nthrough `body`, a merge patch over the App node. `dry_run` checks a change and writes nothing.\n\n| To change | State |\n|---|---|\n| a field read-only | `"edit": false` on its Field, or on the Section, Side, Page or Record that draws it |\n| a column added or dropped | `columns` of the List, every column kept: a List that never stated `columns` draws none beyond the row\'s title and its `records` line |\n| a button | an Act node (\xA7 Act node); `confirm` makes its press ask first |\n| a label | the table\'s field `label` in `set_tables`, for every app; for this app alone, `body: { "words": { "<table>": { "fields": { "fee": "Consultation" }, "options": { "urgency": { "rush": "Same day" } } } } }`; a List\'s `label`, a Section\'s `title`, an Act\'s `label`, `add_label` on the add. A Field\'s `label` heads only a gallery or prose block |\n| the order of nodes and rows | `before` or `after` a sibling, or `in` another parent, in the node\'s entry (a Section ahead of another, a Field ahead of another in its Section); a List\'s `columns` read in the order stated; its rows read in its `sort`: `{ "sort": { "field": "collected_on", "desc": true } }` |\n| the row\'s second line | `row: { "subtitle": ["mechanic"] }` on the List: up to two fields under the title, in place of the table\'s `records` line, which stays as it is |\n| a picker\'s add | `add` on the link Field: `{ "add": ["species", "microchip"] }` asks those fields of the new row; `false` makes the picker only pick. It holds in every picker of that table in the app |\n| a linked row fixed or deleted where its link stands | `record` and `remove` on the link Field, or a row `title` that is a link: `{ "add": ["name", "kind"], "record": ["name", "kind"], "remove": true }` \u2014 the link\'s card opens the row in the drawer, saving those fields, with its Delete. Stated once per table, it holds on every card of that table in the app; a table a List of the app draws is worked in that List, and the statement there is noted and grants nothing |\n| a headline figure | a Reading in the List: `{ "type": "Reading", "in": "orders", "key": "prepaid_owed", "label": "Prepaid owed", "value": "prepaid", "where": { "stage": ["awaiting_payment"] } }` sums the rows in view; without a `value` it counts them |\n| a calendar and its date | `layout: "calendar"` on the List, and the date it places each row on is the first of `row.due`: `{ "layout": "calendar", "row": { "due": ["pickup_on"] } }`; absent, the table\'s `records.due` |\n| a record\'s child lines | a List inside the Record\'s Section over the child table: `{ "type": "List", "in": "details", "entity": "repair_line" }`; the child table (one link back to the record) and the record\'s rollup over its rows come from `set_tables` first. Lines priced off a book: `docs path="design/treatments/moves-and-numbers/priced-lines"` |\n\n**A node the tree leaves to the platform has an address too.** `get_app` lists the nodes the app draws, those the stored\ntree does not state marked `(default)`: the Record with its Sections and Fields, the status buttons, the band of readings.\nPatch one by its address and the call states it first: the nearest node the tree does state gains the default child that\nleads to it, as drawn (the whole Record under a List that states none, with the List\'s `row` picture and the `columns` the\ndefaults added; every Section of a Record that states no part; every status button of the app, each in the Section of its\nstage; every reading of the band, since stating one stands the others down), then the patch applies. Defaults\noutside that subtree stay defaults, and the answer\'s notes say what was stated. Removing every reading, or every status\nbutton, is refused: a set no node states is drawn again by the platform, and only `readings: false` or\n`row.status: { "edit": false }` on the List draws none. **A stated subtree stays as it is**: a\nfield or option the tables gain later is not added to it, so name it in the tree. `{ "due_date": { "edit": false } }`\nover a sparse tree makes that field read-only and takes away `saves <table>: due_date` alone. An address that ends several\ndrawn nodes is refused, as for stated ones.\n\n## What an app may write\n\nAn app states no list of what it writes: the tree derives it, and the generated workflows accept exactly that, so it is\nalso what an agent can do through the app\'s API. `get_app` prints it beside the tree.\n\n1. **A save of field `f` of table `E`** exists when a node draws `f` on a page of `E`\'s rows (a Field in a Record,\n Section, Side or Page, or a part of the head), the table lets a person write it (stored; not computed, stamped or the\n back side of a child\'s link), no Act of the app on `E`\'s rows writes it, and neither the node nor a container above it\n says `edit: false`. A row\'s stage is moved by an Act or the one button per move a status no Act moves gets, by hand only where `row.status` says `{ "edit": true }`, and by none where it says `{ "edit": false }`.\n2. **An add of `E`** exists for a List over `E` that writes its rows unless it says `create: false` (asking what it lists, and\n setting the link back to the record itself), and for an `add` on a Field drawing a picker of `E`. An absent `create`\n stands down where the table states no `singular` (nor the app\'s `words`), or where `where` keeps a select at an option an\n Act sets other than the one a new row starts at; a stated `create` list over such a table is refused. A roster\'s cells and\n a document are added only where `create: true`.\n3. **A Delete of `E`** exists for a List over `E` unless it says `remove: false`, and for the table of every Book of that\n List (a Book has no `remove` of its own). A roster\'s cells, a document and a desk\'s rows are deleted only where `remove: true`.\n `remove: false` takes this app\'s Delete away and no more: the record tools, the API and other apps still delete. A row no\n surface may delete is a table check with `on: ["delete"]`.\n4. **An act** exists for each Act node. Every field it sets, asks, files into, signs or records is the act\'s, and read-only\n everywhere else in the app.\n5. **The table\'s contract holds on every path** and is not restated here: required, unique, no overlap, field rules, table\n checks, stage owners, history and the read scope. (`natural_key` is read by an app\'s add and find-or-create alone.)\n\nDrawing is reading or saving; a List that writes its rows also adds and deletes them, until it says `create: false` or `remove: false`, and a List below a part or a Record that says `edit: false` writes, adds and deletes none.\n\n### What a table gives a component\n\n| Table fact | Feeds |\n|---|---|\n| The title | every row\'s name: Lists, a Record\'s head, link chips, pickers, an add |\n| The row\'s line (`records` subtitle, figure, image, mark, party, description) | the row\'s line and head, cards, a checklist\'s photo |\n| The stage (`records` status) | the stage chips, a List\'s `opens`, the badge, an Act\'s moves, stage owners gating acts, a Section\'s `at` |\n| Deadlines (`records` due, settled, frees) | countdowns and overdue marks, the default sort, attention order, spans on lanes, calendars and rosters |\n| Bounds (`records` limits, gates, tolerance) | the figure\'s meter, wherever it stands |\n| Tasks and cadence (`records` task, cadence) | the ring on task rows, a roster\'s owed days |\n| Applicability and starts (`records` applies, starts) | a field shown and asked only where it applies; an add\'s starting values |\n| A field\'s `required`, `default`, `default_from`, `natural_key`, `unique` | what an add asks, and what it starts at |\n| A field\'s `notation`, `format`, options, `options_where` | money, units, chips, and what a picker offers |\n| The table\'s `read_scope` | which rows each reader reads, in every List |\n\n### Where the rules stand\n\n\xA7 App rules and the rule pages after it are every rule the check holds over an app, by id, whichever component it concerns. A\n**language** is decided by the app\'s own words: a tree names no locale, so what an app draws itself (its buttons, empty\nstates, refusals) is in the language its labels are written in, and `words` count as labels. Mix two languages in one\napp and it answers in two.\n\n## App node\n\nAn app draws its launcher tile, its words, its Lists and its API. It binds the tables it names: `set_app` fills\n`tables` with the id of every table alias the tree uses, and `templates` with that of every document template alias.\nA relabel on either side is a rename and no tree points at another table by accident.\n\n<!-- generated:start node-app -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The app\'s alias, its name among apps; the App node is addressed `/`, and no address in it starts with this key |\n| `name` | text | yes | The job, in the words of the people who do it |\n| `description` | text | no | What the job is for, in a sentence |\n| `icon` | text | no | A Lucide icon name in kebab-case the launcher tile draws |\n| `color` | [colour](#select) | no | The launcher tile\'s colour |\n| `words` | map of alias \u2192 [Table labels](#table-labels) | no | A table \u2192 the words this app draws for it |\n| `scope` | alias | no | A required one-row link of the first List\'s entity: the app works inside one row of the linked entity |\n| `reads` | `"shared"` | no | Every member reads every row, whatever the entity\'s read scope |\n| `uses` | list of alias (at least one) | no | Tables the app\'s Query and Workflow nodes read without drawing them, by alias: they join the tables the app reaches, so a workflow may name them |\n| `period` | `"month"` \\| `"quarter"` \\| `"year"` \\| [Dashboard period days](#dashboard-period-days) | no | The period a dashboard opens on |\n| `tables` | map of alias \u2192 text | no | The id of each table the tree names, by its alias: filled by set_app, never written |\n| `templates` | map of alias \u2192 text | no | The id of each document template the tree names, by its alias: filled by set_app, never written |\n| `children` | list of ([List](#list-node) \\| [Tab](#tab-node) \\| [Reading](#reading-node) \\| [Query](#query-node) \\| [Workflow](#workflow-node)) (at least one) | no | The first List at the root is the register, and a second is another app; a dashboard holds Tabs, Readings and Lists that open in another app. Queries and Workflows stand here too |\n\n#### Table labels\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `label` | text | no | The table\'s name in the plural (a register\'s heading, a block\'s, a link\'s picker) |\n| `singular` | text | no | One row of the table, as the add and empty sentences name it |\n| `fields` | map of alias \u2192 text | no | A field alias \u2192 the words this app draws wherever it names the field; the table\'s field keeps its own label |\n| `options` | map of alias \u2192 map of alias \u2192 text | no | A select field alias \u2192 option alias \u2192 its words |\n\n#### Dashboard period days\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `days` | `30` | yes | The last 30 days through today \u2014 the one span of days a dashboard opens on |\n\n<!-- generated:end node-app -->\n\n- **The first List is the register**: the working list of one table. Every further List at the root is a **log** beside\n it, at most 3, each stating its `label`: read as what was brought into or done to the register\'s rows (the files\n imported, the runs of a batch), opened as a register of its own inside the app, its reads, acts and workflows the\n app\'s. Only the organization\'s members are shown a log. A log is a table or cards with no `Tab`, `Book` or `compose`;\n its table is never the register\'s, a Book\'s or another log\'s; none beside `scope`. A list people work every day as\n a desk of their own is another app.\n- **A dashboard** is an App with no register: it holds `Tab`s, `Reading`s and read-only `List`s that open in\n another app (`app`). One `period` windows every reading placed in time: this month by default, or `quarter`, `year`\n or `{ "days": 30 }`. `words` and `scope` belong to a register.\n- **Query and Workflow** nodes stand at the root and are never drawn.\n- **Writes:** nothing itself.\n\n## List node\n\nA List draws rows of one table. What its rows are depends on where it stands:\n\n- at the App root: the register (the first) or a log (each further one);\n- under a Record, Section, Side or Page: the record\'s child rows, filed through the child\'s one link to the record\n (`via` where it has several);\n- under a roster List: the rows that fill the roster\'s cells;\n- with `same` or `app`, or on a dashboard: related rows, read only.\n\n<!-- generated:start node-list -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Absent, the entity alias |\n| `entity` | alias | yes | The table whose rows the List draws |\n| `via` | alias \\| text | no | The child\'s link to the record, where it has several. A List that only reads may state `<link>.<link>`: rows linked to the record only through a row of their own \u2014 their one-row link to that row, then that row\'s one-row link to the record |\n| `same` | alias | no | A one-row link of the record: lists the rows sharing that link\'s row, the record left out, read only |\n| `where` | [narrowing](#app-tree) | no | A fixed narrowing on the server, for every reader: a select holds one of these options, a yes/no this value, a number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), a field is `{ "empty": true \\| false }`, and `<link>.<field>` keeps the rows whose one-row link names a row holding a select or yes/no so (a path holds nothing on an add) \u2014 every entry ANDed. A List that only reads (`same`, `app`, a Record with `edit: false`) also bounds a date or number by a field of the record it stands beside, `{ "record": "<field>" }`, read when the rows are: a window of the record\'s own days (`{ "published_on": { "gte": { "record": "period_start" }, "lte": { "record": "period_end" } } }`); a bound the record holds blank keeps no row, and the same rows are handed to an Act\'s paper that names the List. A record bound narrows what is listed; it is never an access bound \u2014 the table\'s row rules are. An add holds a select\'s option and a yes/no\'s value; a bound or a blank holds nothing on an add |\n| `expect` | alias \\| [Expect](#expect) | no | One line per option of this select, or per row of this catalog link |\n| `under` | alias | no | A one-row link to the document that covers the line; lines stand grouped under their document |\n| `app` | text | no | The id of the register app its rows open in |\n| `row` | [List row](#list-row) | no | How this List reads its rows, where it differs from the table\'s `records` |\n| `label` | text | no | What the app\'s lists and the tab strip call the register; absent, the entity\'s label. Never the noun of its rows \u2014 the table\'s own label and singular (this app\'s `words`) are |\n| `columns` | list of (alias \\| [Edit column](#edit-column)) | no | Fields read after the row\'s title; `{ field, edit: true }` is the line\'s own control, at most 2. Absent: none, the row reads its title and its `records` line alone |\n| `filters` | list of (alias \\| [Tiered filter](#tiered-filter)) (at most 3) | no | Fields the reader narrows the rows by every day, at most 3; the stage chips always stand. A field is a select, member, link, date, yes/no or number range; `tiers` bands a number or date |\n| `search` | list of alias (at least one) | no | Fields the search box reads; absent, every word of the row |\n| `group` | alias | no | A field the rows stand grouped by; a gantt draws the groups as its lanes |\n| `sort` | [Sort key](#sort-key) \\| list of [Sort key](#sort-key) (at least one) | no | The order of the rows; absent, the first `records.due` soonest, else newest |\n| `opens` | [narrowing](#app-tree) | no | The view the register opens on, a chip the reader may take away |\n| `period` | `"today"` \\| `"week"` \\| `"month"` \\| `"quarter"` \\| `"year"` \\| [Period days](#period-days) | no | The period the List opens on, with Tabs; absent, this month |\n| `attention` | `false` | no | false: rows keep the list\'s own order; absent, those needing attention open first |\n| `readings` | `false` | no | false: the register leads with no band of figures; absent, rows that move, fall due or carry money lead with one. Never with Readings stated |\n| `layout` | [layout](#layouts) | no | How the rows are drawn: a word, or an object stating one layout with its data; absent, a table |\n| `compare` | [Compare](#compare) | no | Reads each row\'s before against its after: two files fields as a pair of photos, or two values of one type as one column |\n| `current` | alias | no | A child List of revisions: the child\'s date or number field that orders them. The row holding the latest value reads as current and the others fold under "superseded \xB7 N", one press to open; the record shows the current file through a lookup of the child ordered by that field |\n| `create` | boolean \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The fields an add asks. Absent: where this List writes its rows, the table\'s required fields, its natural key and its title; `false`: rows are not added here. Where the table states no `singular` (nor the app\'s `words`), or `where` keeps a select at an option an Act sets other than the one a new row starts at, an absent `create` adds nothing; the table\'s `singular` or a stated list adds. A roster\'s cells and a document add only where `true` |\n| `add_label` | text | no | The add button\'s words; absent, the platform\'s word and the entity\'s singular |\n| `remove` | boolean | no | Rows offer a Delete. Absent: they do where this List writes its rows; `false`: this app\'s Delete is gone, while the record tools, the API and other apps still delete \u2014 a row no surface may delete is a table check with `on: ["delete"]`. A roster\'s cells, a document and a desk\'s rows delete only where `true` |\n| `pick` | `true` | no | Also picks rows of the child that no record holds yet |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `children` | list of ([Tab](#tab-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Export](#export-node) \\| [Act](#act-node) \\| [Check](#check-node) \\| [Book](#book-node) \\| [Record](#record-node) \\| [List](#list-node)) (at least one) | no | What the List holds: its Tabs, Readings, Progress, Exports, Acts and Checks, its Books, the Record a row opens, under a roster the List of its cells, and in a child List with `under` the List of the documents the lines stand under (the one place a document is added or deleted) |\n\n#### List row\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `title` | [List row title](#list-row-title) | no | The row\'s title field, stated for its props alone: `edit: false` reads it, `add` is its picker\'s add, `false` its picker only picks; where it is a link, `record` and `remove` how its card works the row it names |\n| `subtitle` | list of (alias \\| [Field](#field-node)) (1\u20132) | no | Up to two fields read under the title, in place of the table\'s |\n| `figure` | alias \\| `false` | no | The one number a row stands for, in place of the table\'s; false, none |\n| `image` | alias \\| `false` | no | A files field that pictures a row, in place of the table\'s; false, none |\n| `due` | list of alias (at least one) \\| `false` | no | Dates that count down, in place of the table\'s; false, none |\n| `status` | `false` \\| `{ "edit": true \\| false }` | no | false: the stage is not shown. `{ edit: true }`: the stage is also moved by hand. `{ edit: false }`: nobody moves it in this app, another app does. Absent, the stage\'s moves are buttons |\n\n#### List row title\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `edit` | `false` | no | false: the title is read; absent, it is written where the table lets a person write it |\n| `add` | `false` \\| `true` \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The new row this field\'s picker offers asks these fields; `true`: the target\'s required fields, its natural key and its title. Stated once per target table, and it holds in every picker of that table in the app. `false`: this link only picks, in every picker of it the app draws, not only where it is written |\n| `record` | list of alias (at least one) | no | The fields of the row this link names that this app saves from the link\'s card, which opens the row in the drawer: stored fields of the linked table a person writes. Stated once per linked table, and it holds on every card of that table in the app, where no List of the app draws its rows. Absent, the card reads the row |\n| `remove` | `true` | no | true: the row this link names is deleted from the link\'s card. Stated once per linked table, with its `record`, and it holds on every card of that table in the app. Absent, the card deletes nothing |\n\n#### Edit column\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The column\'s field |\n| `edit` | `true` | yes | true: the column is the line\'s own control, written in place |\n\n#### Expect\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The child\'s title when the record holds one row per value of it: a single select (one line per option) or a one-row link to a catalog (one line per row) |\n| `of` | alias | no | With a select: the record\'s own multi-select holding the options this record expects a line for, in place of every option |\n| `only` | list of alias (at least one) | no | With a select: the options this List holds a line for; never with `of` |\n\n#### Compare\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `before` | alias | yes | A field of the List\'s rows holding what stood before: a files field (the photo of the place before the work), or a value \u2014 text, a number, a date or a single select (the price a row held before a sheet changed it) |\n| `after` | alias | yes | Another field of the same type holding what stands after: the photo of the same place once the work is done, or the value the row holds now |\n| `edit` | `true` | no | The after is written on the line |\n\n#### Tiered filter\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A number \u2014 stored, or a formula or rollup reading one \u2014 in one unit for every row |\n| `tiers` | list of number (at least one) | yes | The breakpoints between its bands, ascending, in the field\'s stored unit: under the first, from each to the next, from the last up \u2014 each band worded in the field\'s notation; a filter picks several at once |\n\n#### Sort key\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The field rows are ordered by |\n| `desc` | `true` | no | Latest or largest first; absent, soonest or smallest first |\n\n<!-- generated:end node-list -->\n\nA List takes only the props its place lists:\n\n<!-- generated:start list-places -->\n\n| A List standing in | Takes |\n|---|---|\n| the register (the first List of the App) and a log (each further one) | `key`, `entity`, `row`, `label`, `columns`, `filters`, `search`, `group`, `sort`, `opens`, `period`, `attention`, `layout`, `create`, `add_label`, `remove`, `readings`, `where` |\n| a record\'s child rows, as a table, a checklist or a desk | `key`, `entity`, `via`, `label`, `columns`, `create`, `add_label`, `where`, `under`, `filters`, `search`, `group`, `sort`, `remove`, `pick`, `layout`, `compare`, `current`, `when`, `expect`, `row` |\n| a record\'s child rows with `layout` an agenda | `key`, `entity`, `via`, `label`, `columns`, `create`, `add_label`, `remove`, `layout`, `when`, `row` |\n| a record\'s child rows with `layout` `"timeline"` | `key`, `entity`, `via`, `label`, `create`, `add_label`, `remove`, `layout`, `when`, `row` |\n| a record\'s related rows, read only (`same`, `app`, or a Record that is `edit: false`) | `key`, `entity`, `via`, `label`, `where`, `columns`, `app`, `same`, `when`, `row` |\n| the documents a child List\'s lines stand under (`under`), a List inside it | `key`, `entity`, `create`, `remove` |\n| the cells of a roster, a List inside it | `key`, `entity`, `expect`, `create`, `remove`, `row` |\n| a dashboard\'s list, read only | `key`, `entity`, `label`, `where`, `columns`, `app` |\n\n<!-- generated:end list-places -->\n\n- **Children:** Tab, Reading, Progress, Export, Act, Check, Book, Record, and a List for a roster\'s cells. A Check or an\n Act stands in the List whose rows it works on: on each row (its \u22EF and the open row\'s), or with `on` on the ticked rows\n or every row in view.\n- **A child List\'s Record** holds `edit`, Fields and Lists only: its Fields are what the rows\' saves write, and a row\n opens in place under itself \u2014 its fields, then each List of its own children (an entity linking to the List\'s), stated\n as a child List one level up with its Acts, Checks and Record, at any depth. With `edit: false` the List only reads, as\n a related-rows List does: it states its `label`, takes none of `filters`, `search`, `group`, `sort`, `create`,\n `remove` or `layout`, and may reach rows linked to the record only through a row of their own (`via:\n "<link>.<link>"`).\n- **Documents:** in a child List with `under`, a List over the document\'s table (`key`, `entity`, `create`, `remove`,\n Acts, Checks and a Record of the document\'s Fields and Lists) is the one place the documents are added or deleted;\n `create` states `true`, and `remove` needs `create: true` beside it.\n- **Writes:** a List that writes its rows adds and deletes them \u2014 `create: false` and `remove: false` turn each off \u2014 `pick` links existing rows, an `edit: true` column writes that\n field on the line, and a layout move (a gantt bar, a lane\'s block, a plan\'s item) writes only fields the app already\n writes on the row\'s page.\n- **`create` asks:** `true` asks the title, the natural key and every required field a person writes \u2014 never a computed\n or stamped field, nor the link the place already sets. An `expect` list also asks the key it makes its lines by, and\n their picture. A list of fields overrides it; `false`, rows are not added here.\n- **`row`** overrides what the table\'s `records` say of the register\'s rows in this List alone (a cashier\'s figure is what\n is still owed): the subtitle, figure, image and deadlines stated replace the table\'s. A child List, a related List and\n a roster\'s cells read their rows as their table does, so their `row` states only a picker\'s add and a link\'s card (on its title or\n subtitle) or the stage moved by hand (`row.status`).\n- **`filters`** are the fields a reader narrows by every day, none to three \u2014 a quota never; each earns its place by\n daily use. A date filters by a range of days, a number by a range in its unit, `{ "field", "tiers" }` by the bands its\n breakpoints cut. A threshold the business names ("large orders") is a formula yes/no, never a range. The stage chips\n always stand.\n- **`opens`** is the view the register opens on, each entry a chip the reader may take away; absent, the open work\n (rows not at a `closed` stage).\n- **`expect`** makes one line per option of a select, or per row of a catalog link, and an empty line adds its row\n (a checklist of papers per case). **`under`** groups lines under the document that covers them.\n- **`compare`** reads each row\'s before against its after: two files fields as a pair of photos, or two values of one\n type as one column under the after\'s label.\n\n## Layouts\n\nA List\'s `layout` is how it draws its rows: a word (`"table"`, `"cards"`, `"calendar"`, `"roster"`, `"timeline"`) or an\nobject stating one layout and the data it reads (`{ "gantt": \u2026 }`, `{ "lanes": \u2026 }`, `{ "plan": \u2026 }`, `{ "agenda": \u2026 }`,\n`{ "checklist": \u2026 }`, `{ "desk": \u2026 }`). Absent, a table: the word `"table"` is the register\'s, and a child List states a\nchecklist, a desk, an agenda or `"timeline"`, else leaves `layout` out. Each layout reads what the rows already state \u2014 their\n`records` line, stage and dates \u2014 and the keys a layout adds stand on its section: here for an agenda and a checklist, \xA7 Time and place layouts for the rest, \xA7 Publish desk for a desk. Which one a job needs: the\n`design` reference. The register lays out as a table, cards, a calendar, a roster, a gantt, lanes or a plan; a record\'s\nchild rows as a table, a checklist, a desk, an agenda or a timeline.\n\n### Table\n\nAbsent `layout`, each row is one line: the table\'s `records` entry \u2014 its picture or mark, title and the line under it \u2014\nthen the stage, the `columns`, and the figure at the right; the title, the stage and a column of days or amounts sort\nthe rows by their header. A table groups its rows by `group`; a table or cards reads `Tab`s and `Book`s.\n\n### Cards\n\n`"cards"` draws each row as a card led by its picture \u2014 the `records` `image`, or the List\'s `row.image`: its title and\nthe line under it, its stage and what needs the reader (a deadline, a warning), then its figure and the `columns` at its\nfoot. Cards are for rows found by their picture (a product, a vehicle, a damage photo); rows with no picture read better\nas a table.\n\n### Calendar\n\n`"calendar"` places each row on its day \u2014 a month, a week, a day or a list (a phone reads the list or a day): at its\nhour where the date holds one, over its span where the row\'s line holds a second date after it, its line\'s other words\nand the `columns` after its title. The calendar reads the window in view, and its count and the stage chips count that\nwindow, opening on every row.\n\n### Roster\n\n`"roster"` draws the List\'s rows down the side and the days of a week or a month across; the roster\'s cells are a List\ninside it over the table whose rows fill the days \u2014 each stands on one row by its single link to it, and on the first\ndate of its line (a shift on a person\'s day, a run on a vehicle\'s), on every day through the second date where its line\nholds one. A cell reads its row\'s stage, else the first single select on its line (a shift\'s kind), several rows their\ncount. A day holding none stays empty: nothing was due, which is never an absence. A filled cell opens its row; an empty\none adds a row there, the register row and the day set. With `expect` on the cells (a single select of those rows) the\ncolumns are its options in place of days \u2014 a checklist of papers \u2014 and `of` names the ones each row expects: a gap among\nthem reads as one and adds its row, the others stand blank, and each row reads what it holds of them, `2/3`. Over days, a\nroster reads the `cadence` its table\'s `records` states (a site\'s diary page, Monday to Saturday from breaking ground to\nhandover): a past day owed with none is marked missing and counted, and each row reads the days it holds of those owed. A\nroster states no `columns` and needs no readings: its legend counts each state in view.\n\n```json\n{ "type": "List", "entity": "operator", "layout": "roster", "filters": ["depot"], "children": [{ "type": "List", "entity": "run" }] }\n```\n\n### Timeline\n\n`"timeline"` reads a record\'s child rows as a log of dated entries, newest first, with a composer where the app adds\nentries, each corrected and removed where it stands. It is never the record\'s status history: the rail shows each\nmove. A timeline\'s moment is its child\'s first date, and who its member field. Rows each what happened on a day and who \u2014\na date on their line, a member, no stage and no figure \u2014 read best as a timeline.\n\n### Agenda\n\n`{ "agenda": { \u2026 } }` reads a record\'s child rows as planned entries on a time axis: by day, soonest first, each with its\npicture and the day that is today marked. The day is the first date on the child\'s row line (its `records` title or\nsubtitle); within a day entries follow a datetime there, else the first single select after the date. An entry with no day\nyet is a plan, listed first and dated in place. `start` is the record\'s date that is the first day: each day\'s heading\ncounts from it (Day 1, Day 2).\n\n<!-- generated:start layout-agenda -->\n\n#### Agenda keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | no | The record\'s date that is the first day: each day\'s heading counts from it; absent, the weekday and the date alone |\n\n<!-- generated:end layout-agenda -->\n\n### Checklist\n\n`{ "checklist": { \u2026 } }` reads a record\'s child rows, which are tasks, as a checklist of checks: each one\'s mark \u2014 passed\nat a `closed` option, failed at a `failed` one, else still to check \u2014 its title, its columns as its words (a text column\nits note, on its own line), and on a line under its title its photo and its clause. Never grouped nor written in place.\n`passed` alone sets the mark, so rows with no `task` and no status are checks: a measured yes/no is the verdict. With\n`expect` on the standard\'s items, each item is a line marked the same way (a missing one still to check), filled in\nplace by the block\'s `columns` (a measured value, a note, a photo), so `photo` and `clause` are not named.\n\n<!-- generated:start layout-checklist -->\n\n#### Checklist keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `photo` | alias | no | The child\'s files field holding the photo each row is checked by; absent, the child\'s `records.image` |\n| `clause` | alias | no | The child\'s one-row link to what each row is checked against \u2014 a standard\'s clause, a rule |\n| `failed` | list of alias (at least one) | no | Options of the child\'s status a row stands failed at \u2014 none of them closed |\n| `passed` | alias | no | The child\'s yes/no field (stored or a formula) setting each row\'s mark: yes passed, no failed, empty still to check |\n\n<!-- generated:end layout-checklist -->\n\n## Time and place layouts\n\nThe layouts that place a List\'s rows in time or on a plan. Each is a `layout` object stating the data it reads.\n\n### Lanes\n\n`{ "lanes": { "by": \u2026 } }` draws each row of the linked table as a lane (a chair, a machine, a room), empty ones too,\nand each row of the List as a block from the first date on its line to the second: over the hours of one day where the\nfirst holds its hour, else over the days of one day, a week or a month. A block reads the row\'s name and the first of its\n`columns`; a free stretch between blocks adds a row there, its lane and its start set, and its end where the next block\nbounds it. Rows naming no lane stand first, in a lane of their own; where the app writes the link, each block moves to\nanother lane from its menu. Inside a `scope`, the lanes are the ones whose own one-link names that scope\'s row. A span\'s\nend holds its day unless `records` names it the day the row `frees`.\n\n`loads` names what a lane carries: `{ "weight": "payload", "volume": "box" }` sums each block\'s number and reads it\nagainst the lane row\'s own, at most two. A lane holds its blocks while they stand, so it reads the most they add up to at\none time in the window, named, past its bound, by how much and on which day or hour. The two are in one unit, or two of\none dimension. A bound a single row must keep (no parcel heavier than its van) is a `max` on the field through the link,\nwhich refuses the move.\n\n<!-- generated:start layout-lanes -->\n\n#### Lanes keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `by` | alias | yes | A one-link of this entity whose target\'s rows are the lanes (a chair, a machine, a room), each drawn even when empty |\n| `loads` | map of alias \u2192 alias | no | What a lane carries against what it holds, at most 2: a number of this entity each block adds \u2192 a number of the lane\'s row it is held within, in one unit or two of one dimension |\n\n<!-- generated:end layout-lanes -->\n\n### Gantt\n\n`{ "gantt": { \u2026 } }` draws each row as one bar on one axis of time: `{ "start": "etd", "end": "eta", "milestones":\n["cut_off"], "planned": { "end": "booked_eta" }, "progress": "done", "after": "waits_on" }`. `end` defaults to the table\'s\nfirst `due`, and a table with neither is refused; `start` and `end` are two dates of the row, both days or both moments,\nand each planned date is read as `start` is. `progress` is a percent (0\u2013100); absent, a row whose record lists its tasks\nfills to how many are done. `after` is a link to rows of the same table, each ending before the row starts. The bars\nstand in the lanes of the List\'s `group`. Where the app writes the row and a person writes a bar\'s date, the bar is\ndragged through the row\'s save; a computed date, one an Act sets, or a row the app does not write drags nothing.\n\n<!-- generated:start layout-gantt -->\n\n#### Gantt bar\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | yes | A date of this entity each row\'s bar starts at (a sailing, a hire\'s start, a task\'s start) |\n| `end` | alias | no | The date each row\'s bar ends at, read as `start` is \u2014 two days or two moments; absent, the entity\'s first `due` |\n| `milestones` | list of alias (at least one) | no | Dates of the row marked on its bar\'s line as diamonds, each named by its label (a cut-off, a delivery, the end of free time) \u2014 never `start` or `end` |\n| `planned` | [Gantt plan](#gantt-plan) | no | The plan each row is read against, drawn as a thin bar under its own \u2014 each date read as `start` is; a missing one is the bar\'s own |\n| `progress` | alias | no | A percent of this entity (0\u2013100) filling each row\'s bar as far as the work is done; absent, how many of the row\'s tasks are done fills it where its record lists them, else the bar wears its status\'s tone |\n| `after` | alias | no | A link of this entity to its own rows each row waits on \u2014 it starts once they end (finish-to-start), an arrow from each; a row starting before one ends is drawn in the danger tone |\n\n#### Gantt plan\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | no | The date the row was planned to start at |\n| `end` | alias | no | The date the row was planned to end at (a booked arrival) |\n\n<!-- generated:end layout-gantt -->\n\n### Plan\n\n`{ "plan": { \u2026 } }` draws things that stand at a place \u2014 pallets in a warehouse \u2014 on a plan of the places. The places are\nthe rows of a table whose `records` states a `plan_place` (a bay, an open area or a landmark, each at its spot on a sheet\nof the plan); `at` names the one-link of the List\'s rows to a place and, for an item in a bay, the row (`across`) and the\ntier (`up`) it stands in, counted from 1. A bay is drawn as its rows and tiers, an area with a count, a landmark as a\nlabelled frame; the reader sees a plan, one bay enlarged, and the same plan in 3D. The List may read other tables as\n`Book`s: one plan of every table\'s items. Each item carries `__reach` \u2014 how many items stand in the way of taking it out\n(those above it where the place is `stacked`, and those in the rows between it and the side `reach_from` says the bay is\ntaken from) \u2014 so the list, its export and an agent\'s read all carry it; `<table>_plan_load` counts the rows each place\nholds. A move through the app is refused unless it clears the place, row and tier all three, or places the item at\na free cell of a bay \u2014 resting on a present item where the place is `stacked` \u2014 or at an area. Every other write (a grid edit, an import, a tool) holds one item to a cell only where the item\'s table states `unique: [<at>, <across>, <up>]`; a plan over a table stating none is noted. A plan opens on every row\nits `where` keeps, with no stage chip narrowing it. Rows with no place wait in a tray beside the plan. An Act that asks exactly a place (a link to the plan\'s places) and two numbers (the row and the tier) is answered by a pick on the plan, never by a form. On a desk a press opens a row\'s record in the panel beside the plan.\n\n```jsonc\n// table "bay"\n"title": "code",\n"records": { "plan_place": { "kind": "kind", "x": "x", "y": "y", "across": "rows", "up": "tiers", "reach_from": "taken_from", "stacked": "is_stack" } }\n```\n\n```json\n{ "type": "List", "entity": "pallet", "filters": ["line"], "layout": { "plan": { "at": "bay", "across": "row", "up": "tier" } } }\n```\n\n```jsonc\n// table "pallet": one item to a cell on every write, not only through the app\n"unique": [["bay", "row", "tier"]]\n```\n\n<!-- generated:start layout-plan -->\n\n#### Plan keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `at` | alias | yes | A one-link of this entity to an entity whose `records` states a `plan_place`: where each row stands |\n| `across` | alias | yes | A number field: the row of a bay a row stands in, counted from 1; empty at an area |\n| `up` | alias | yes | A number field: the tier of a bay a row stands at, counted from 1 at the ground; empty at an area |\n| `fill` | alias | no | A single select the items are coloured by; absent, the first single select among the register\'s filters |\n| `mark` | [Plan mark](#plan-mark) | no | A row holding the option says the option\'s label on its box and is hatched in the option\'s own colour, and the legend names it by the label; in books, the select is matched by option alias, and a book lacking the select never marks its rows |\n| `size` | [Plan size](#plan-size) | no | Each row\'s box as it stands in the world, in the one length unit its places\' `size` states: the option the row holds of the select names its box\'s width across, depth along and height; in books, matched by option alias, as `mark` is; a row holding an option it does not name is drawn at its place\'s cell |\n\n#### Plan mark\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A single select of this entity |\n| `option` | alias | yes | An option alias of that select |\n\n#### Plan size\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A single select of this entity |\n| `options` | map of alias \u2192 [Plan size box](#plan-size-box) | yes | The box of each option alias of that select |\n\n#### Plan size box\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `across` | number | yes | The box\'s width across the bay\'s rows |\n| `along` | number | yes | The box\'s depth along them |\n| `up` | number | yes | The box\'s height |\n\n#### Plan place\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `kind` | alias | yes | A single select, options aliased exactly `bay`, `area`, `landmark`: a bay holds rows and tiers, an area neither, a landmark nothing |\n| `sheet` | alias | no | The plan sheet it is on: a single select, or a one-row link to an entity whose `records` states its `image` \u2014 the sheet\'s drawing, which the places are drawn over (with `scale`) |\n| `scale` | alias | no | A number field of the sheet entity `sheet` links to: how many cells wide its drawing is, the plan\'s scale. Required where that entity states an `image`, and only then |\n| `x` | alias | yes | A number field: left edge, in cells |\n| `y` | alias | yes | A number field: top edge, in cells |\n| `width` | alias | no | A number field: an area\'s frame width, in cells |\n| `depth` | alias | no | A number field: an area\'s frame depth, in cells |\n| `across` | alias | yes | A number field: a bay\'s rows |\n| `up` | alias | yes | A number field: a bay\'s most tiers |\n| `reach_from` | alias | no | A single select, options aliased exactly `first`, `last`, `both`: the side boxes leave a bay from; a bay that names none has no reach and no arrow |\n| `stacked` | alias | no | A boolean field: true where items at this place rest on the one below (a stack), so a tier above the first needs a present item under it, shows floating without one, and an item\'s reach counts the items above it; false or empty, items sit at any tier and only the rows between a bay\'s reach side and the item are in the way |\n| `size` | [Plan place size](#plan-place-size) | no | The real size of ONE cell of the place, for 3D: three number fields stating one length `unit` alike. Absent, a cell is drawn at the size most of the sheet\'s places state, a unit cube where none states one |\n\n#### Plan place size\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `across` | alias | yes | A number field: the width of one cell across the rows |\n| `along` | alias | yes | A number field: the depth of one cell along the place |\n| `up` | alias | yes | A number field: the height of one cell, one level |\n\n<!-- generated:end layout-plan -->\n\n## Publish desk\n\nRows that are each a place the record is sent \u2014 a notice\'s boards, a listing\'s portals \u2014 are a record\'s child List with\n`expect` on the child\'s one-row link to the catalog of those places and `layout: { "desk": \u2026 }`:\n\n```json\n{\n "type": "List",\n "entity": "posting",\n "expect": "board",\n "columns": ["posted_link", "refusal"],\n "layout": { "desk": { "bodies": ["wording", "wording_abroad"], "text": "goes_out_as", "media": "pictures", "link": "page", "group": "reach", "network": "kind", "networks": { "company_page": "linkedin" } } }\n}\n```\n\nThe desk is where the post is written. `bodies` are the record\'s plain text fields it is written in (one per language,\nsay), each its editor between the list and the preview, saved as any field of the record is and drawn nowhere else on it.\nThe preview is `text` read over what is on screen: where `text` is a formula of the child, the desk computes that same\nformula over what is typed, so the post moves as it is written, and a place not ticked previews the words its row would\nhold. For that the formula reads only what a screen holds whole: stored values of its row, the record through lookups over\nthe link back to it, the place through lookups over `expect`, and formulas of the row over those \u2014 a rollup, a lookup\nthrough another link or the clock is refused, naming the field.\n\nEvery place the link admits is a line with a tick and that place\'s face: its picture, else the mark of the network it is on\nwhere `networks` names one. `group`, another single select of the place, stands the lines under its options in their\norder: a kind holding a ticked line is open, one holding none folded to its heading and its count. A tick makes the line\'s\nrow from its key alone \u2014 so nothing else the child requires is left without a default \u2014 and an untick deletes it until the\nrow\'s stage is `closed`: what went out is not taken back. It asks first only where a person wrote on the row. One line is\nworked beside the list and read as the post that place\'s feed shows: `text` is the child\'s words for that place, `media`\nand `link` the record\'s, and `network` the place\'s single select whose options `networks` names to a feed; an option not\nnamed reads on a plain card. Under the bodies stand the List\'s `columns` a person writes of the line\'s row; under the post,\nthe press copying its words beside the Acts of the child, then what the line and the `columns` hold that nobody writes\nthere (the link it went out at, why it was refused). A value an Act or a workflow fills is stated as that Act\'s, so the\ndesk never offers it as an empty box. Sending is an ordinary Act of the record.\n\n<!-- generated:start layout-desk -->\n\n#### Desk keys\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `bodies` | list of alias (1\u20133) | no | The record\'s plain text fields the post is written in, in order \u2014 one per language, say \u2014 at most 3: each its editor on the desk, between the list and the preview, and drawn nowhere else on the record |\n| `text` | alias | yes | A text of the block\'s rows \u2014 stored, or a formula yielding text \u2014 holding the words that go out on that row\'s destination. A formula is computed on the desk too, over what is typed: it reads stored values of its row, the record through lookups over the link back to it, the destination through lookups over `expect`, and formulas of the row over those \u2014 never a rollup, another link or the clock |\n| `media` | alias | yes | A files field of the record: the pictures or the video the post carries |\n| `link` | alias | no | A text field of the record holding a link (`format: "link"`), carried as the post\'s card |\n| `network` | alias | yes | A single select of the entity `expect` links to, saying which network each destination is on |\n| `group` | alias | no | Another single select of the entity `expect` links to \u2014 the destination\'s kind \u2014 the lines stand grouped by: each option a heading in the select\'s order, a group holding no ticked line folded to its heading and its count, destinations holding no option last |\n| `networks` | map of alias \u2192 `"facebook"` \\| `"instagram"` \\| `"threads"` \\| `"x"` \\| `"linkedin"` | yes | That select\'s options, each by its alias, to the network whose feed the post is previewed in \u2014 facebook, instagram, threads, x, linkedin. An option not named has no feed to imitate: its words read as a plain card |\n\n<!-- generated:end layout-desk -->\n\n## Record node\n\nA Record draws what a row opens: the head, then the parts.\n\n- **The head:** title, subtitle, picture, mark, party, stage badge, figure and meter, deadlines, description \u2014 always the\n table\'s `records` facts, overridden by the List\'s `row`. A field the head draws is never also a Field of the record.\n- **No Record node:** under the register, the platform designs the record\'s Sections from the rows: the fields the\n head does not draw, child Lists, papers and Acts. A stated Record stands exactly as written, so it replaces them.\n Under a child List, an absent Record is every field of the child except the link back.\n- **Children:** either parts \u2014 `Section`, `Side`, `Page` \u2014 or, for a one-part record, `Field`, `List`, `Reading`,\n `Progress`, `Statement` and `Act` directly. The two are never mixed. Below the register \u2014 a child List\'s, a document\'s\n or a roster cell\'s \u2014 a Record holds `edit`, the Fields its saves write and the Lists of its rows\' own children; a\n Book\'s holds Fields and `edit` only.\n\n<!-- generated:start node-record -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `door` | `"page"` \\| `"drawer"` \\| `"beside"` | no | Absent, a page where a part is a step, a Side or a Page or there are more than 3 parts, else a drawer |\n| `comments` | `true` | no | The record carries comments; absent, nobody comments |\n| `edit` | `false` | no | false: nothing the record draws is written |\n| `compose` | [piece](#composed-page) | no | The page stated as a tree of finished pieces; a Record with `compose` holds no children |\n| `children` | list of (alias \\| [Field](#field-node) \\| [Section](#section-node) \\| [Side](#side-node) \\| [Page](#page-node) \\| [Outside](#outside-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | Parts (Section, Side, Page), or for a one-part record its Fields, Lists, Readings, Progress, Statements and Acts directly \u2014 never both; Outside parts stand beside either. Absent, the platform designs the Sections from the rows: the fields the head does not draw, child Lists, papers and Acts. Stated, it stands exactly as written |\n\n<!-- generated:end node-record -->\n\nThe door is a page where a part is a step, a Side or a Page, or where there are more than 3 parts; else a drawer. `door`\nis a judged choice of how a job is worked (one after another over a long record), so it is kept. From three parts on, the\nrecord is navigated by their titles: a page by a rail that jumps to each (a strip of the same names on a phone), a drawer\nby the same strip pinned over its scroll; with fewer, the parts just stack.\n\n`comments: true` lets people comment, and is opt-in: state it only where the job\'s people discuss a record. A door in the\nrecord\'s head then opens the comments in a panel beside it, with one composer. A record shows no activity and no audit\nlog: a history an app shows is table-backed \u2014 `records.status.dates`, `records.status.history`, an event table an act\nmakes \u2014 stated where the app shows it.\n\nA field, a Reading and an Act is each placed once on a record: the head, or one part. A Section or a List with `when` is\ndrawn only while each named select of the record holds one of its options.\n\nA page the parts cannot say is a **composed page** (\xA7 Composed page): `compose` states the record as a tree of finished\npieces, and the Record holds no children.\n\n## Section node\n\nA titled part of a record, and optionally a step of its path. A Section draws its Fields, Lists, Readings, Progress,\nStatements and Acts in reading order; its Acts stand at its foot.\n\n<!-- generated:start node-section -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique among its siblings; part of the node\'s address. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The part\'s heading |\n| `description` | text | no | A line under the heading |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `edit` | `false` | no | false: nothing in this part is written |\n| `at` | list of alias (at least one) | no | Options of the record\'s status during which this part is the work now: a step of its path |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | What the part draws, in reading order; its Acts stand at its foot |\n\n<!-- generated:end node-section -->\n\n- **`at`** names the stage options during which the section is the work now \u2014 only on a record that\n truly moves through those stages: the rail marks it done, now or waiting, and the section stands even holding nothing\n yet. It never hides a field, locks it or gates an act.\n- **Acts:** every Act on a record stands in the Section whose work it is. An Act moving the stage and offered at ONE step\n stands at that step\'s foot when it moves the work forward, to an exit later in the stage\'s options, or back to a step the\n work passes anyway; one offered at several steps, or moving back into a detour, stands in the Section it enters. A\n correction (`danger`, and not one of its step\'s outcomes) waits in the Section heading\'s \u22EF.\n- **A detour** \u2014 a stage the work only steps aside into from one step and leaves only back to it (a pause, a repair) \u2014\n stands before that step, its option and its Section alike.\n- **An exit** stands only once the record is in it, its reason there and its rail item apart from the path; until then the\n Act waits in the header\'s \u22EF.\n- **Writes:** its Fields and its Acts.\n\n## Side node\n\nThe one part drawn beside the rest \u2014 the photos looked at, the pool picked from. At most one Side to a record, on a page\ndoor, and never also a Page.\n\n<!-- generated:start node-side -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique among its siblings; part of the node\'s address. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The part\'s heading |\n| `description` | text | no | A line under the heading |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `edit` | `false` | no | false: nothing in this part is written |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | What the part draws, in reading order; its Acts stand at its foot |\n\n<!-- generated:end node-side -->\n\nIt holds what a Section holds. Writes: its Fields and its Acts.\n\n## Page node\n\nA part on its own page, off the record\'s scroll, with a way back \u2014 rarely, for a long related list or a workspace of its\nown that would make the record worse stacked. Never on a drawer, and never a step: its title names an address no other\npage shares (a letter or a digit at least), and not every part is a Page.\n\n<!-- generated:start node-page -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique among its siblings; part of the node\'s address. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The part\'s heading |\n| `description` | text | no | A line under the heading |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n| `edit` | `false` | no | false: nothing in this part is written |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Reading](#reading-node) \\| [Progress](#progress-node) \\| [Statement](#statement-node) \\| [Act](#act-node)) (at least one) | no | What the part draws, in reading order; its Acts stand at its foot |\n\n<!-- generated:end node-page -->\n\nIt holds what a Section holds. Writes: its Fields and its Acts.\n\n## Outside node\n\nWhat one record shows, and which acts it allows, to a person with no seat who opens a link a member made of it. A member\npresses Create link on a record while its status stands in `at`; the link opens that one record until it expires, is\nrevoked, or its act moves the status out of `at`. The person sees the stated Fields and Lists and presses the stated Acts\nand nothing else. It stands directly in a Record of a table with a stored status select: the register\'s, or the Record of\na List below the register, whose rows the link then opens.\n\n<!-- generated:start node-outside -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | no | Unique in the app: a link to the record names it. Absent, the title\'s words as a slug, written once and kept |\n| `title` | text | yes | The heading of the page the person without a seat opens |\n| `description` | text | no | A line under the heading |\n| `at` | list of alias (at least one) | yes | Options of the record\'s status during which a link to it opens; none is owned by a stage, and each Act moves the status out of them |\n| `children` | list of (alias \\| [Field](#field-node) \\| [List](#list-node) \\| [Act](#act-node)) (at least one) | yes | Exactly what the link shows and allows: Fields read as values, Lists of child rows read as they are, and the Acts the person may press, each moving the status out of `at` |\n\n<!-- generated:end node-outside -->\n\n- **Fields** are read as values, in order; a lookup, a rollup or a formula is computed for the link only if stated here.\n A Field states nothing but its alias.\n- **Lists** read the child rows under the record as they are: `columns`, `label` and `where`.\n- **Acts** are the link\'s alone: no member, visitor or agent presses them. Each states `key`, `label`, `confirm`, `when`,\n `requires`, `asks`, `set`, `template` with `into`, or `sign`. `when` absent is the status held in `at`; `set` moves the\n status to an option outside `at`, so each press ends the visit. `asks` are text, numbers, dates or listed options. A\n `sign` is by a text field the signer types, with no witness: the link is the attestation.\n- `at` names options no stage owns, and no two Outsides of a table open during the same option.\n- **Writes:** its Acts, and nothing else.\n\n## Tab node\n\nA Tab is one view of a List\'s rows over a period, or one sheet of a dashboard.\n\n- **In a List:** a register reporting a period \u2014 what stands at its end beside what came and went during it (the stock,\n the hires out, the tickets open) \u2014 reads its rows as Tabs over one `period` the reader picks above them. A Tab holds\n the rows `dated` within the period, those `open` at its end, or lists every row and draws what a child\'s rows came to\n in the period (`totals`). A Tab narrows its rows and holds no nodes; Readings stand beside it in the List and read the\n open Tab, or the one their `tab` names. Never date chips set by hand, and never a Tab per stage, which the chips split.\n- **On a dashboard:** a Tab holds its Readings and Lists as children. A dashboard with Tabs (2 to 5) draws a strip under\n its title that picks one, the first open.\n\n<!-- generated:start node-tab -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | Unique among its siblings; part of the node\'s address |\n| `label` | text | yes | What the tab holds, in the reader\'s words |\n| `dated` | alias | no | A date: the tab holds the rows dated within the List\'s period |\n| `open` | [Tab open](#tab-open) | no | The tab holds the rows open at the period\'s end: begun, and not yet ended |\n| `totals` | [Tab totals](#tab-totals) | no | Each row\'s figures over a child\'s rows in the period |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `sort` | [Sort key](#sort-key) \\| list of [Sort key](#sort-key) (at least one) | no | The order this tab\'s rows read in; absent, the List\'s |\n| `children` | list of ([Reading](#reading-node) \\| [List](#list-node)) (at least one) | no | On a dashboard: the Readings and Lists this sheet draws. A Tab of a List holds none: Readings stand beside it in the List |\n\n#### Tab open\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `from` | alias | yes | The date a row begins on |\n| `to` | alias | yes | The date a row ends on, empty while it is open |\n\n#### Tab totals\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `rows` | alias | yes | An entity of this model whose rows each belong to one row of the register\'s entity: the rows the columns add |\n| `on` | alias | yes | A date of the child placing each row in time: stored, a formula yielding a date, or a lookup of a date through a one-row link \u2014 never a moment or a span of days |\n| `columns` | list of [Totals column](#totals-column) (1\u20136) | yes | The figures drawn in place of the register\'s columns after each row\'s name, each under its own alias and label |\n\n#### Totals column\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `alias` | alias | yes | The column\'s name: its address in the totals read |\n| `label` | text | yes | What the column holds, in the reader\'s words |\n| `value` | alias | no | A number of the child (stored, a formula or a rollup) summed over the rows; absent, the rows are counted. A figure read in each row\'s own unit or currency stands only where the child\'s select is a lookup, through `via`, of a single select of the register\'s entity |\n| `where` | map of alias \u2192 (list of alias (at least one) \\| boolean) | no | Only the child\'s rows whose select holds one of these options, or whose yes/no is this value \u2014 every entry ANDed |\n| `window` | `"before"` \\| `"in"` \\| `"through"` | yes | Which of the child\'s rows, by `on`, the column adds: `before` those dated before the period\'s first day, `in` those from its first day through its last, `through` those on or before its last day |\n\n#### Period days\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `days` | integer | yes | How many days through today, at most 366 |\n\n<!-- generated:end node-tab -->\n\nA `totals` Tab adds, per column, the child\'s rows `before` the period\'s first day, `in` the period, or `through` its last\nday, so `before` plus `in` of one value is its `through`:\n\n```json\n{\n "type": "Tab",\n "key": "items",\n "label": "Items",\n "totals": {\n "rows": "ledger_line",\n "on": "moved_on",\n "columns": [\n { "alias": "opening", "label": "Opening", "value": "quantity", "window": "before" },\n { "alias": "received", "label": "Received", "value": "quantity", "where": { "kind": ["receipt"] }, "window": "in" },\n { "alias": "issued", "label": "Issued", "value": "quantity", "where": { "kind": ["issue"] }, "window": "in" },\n { "alias": "closing", "label": "Closing", "value": "quantity", "window": "through" }\n ]\n }\n}\n```\n\n## Field node\n\nA Field draws one field of the record it stands in. How it is drawn follows from the field\'s type and the table\'s facts:\n\n- a control or a value, with its notation, money, units and deadline countdown;\n- a files field as pictures and documents, a long or markdown text as prose written in place \u2014 a gallery or a prose block,\n with a heading (`label`) and a condition (`when`) of its own; a run of files Fields is one gallery;\n- a link as its row\'s chip, with a picker narrowed by `options_where`, and `add` for the new row the picker offers;\n- the back side of a child\'s link as the rows filed under the record, added to with `add`;\n- the fields an Act\'s `sign` writes as one signature.\n\n<!-- generated:start node-field -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | The field\'s alias |\n| `edit` | `false` | no | false: the field is drawn and read; absent, it is written where the table lets a person write it |\n| `label` | text | no | The heading of the field\'s gallery or prose block, where it draws as one; absent, the field\'s label. A run of files fields takes the first one\'s |\n| `block` | boolean | no | true: a files or text field draws as its own gallery or prose block; false: as a control among the part\'s fields. Absent, files and markdown fields draw as blocks and plain text as a control |\n| `when` | [condition](#app-tree) | no | A gallery or prose block is shown only while the record\'s selects hold these options, its yes/nos these values, its numbers and dates meet their bounds and its `{ "empty": true \\| false }` fields are blank or filled |\n| `add` | `false` \\| `true` \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The new row this field\'s picker offers asks these fields; `true`: the target\'s required fields, its natural key and its title. Stated once per target table, and it holds in every picker of that table in the app. `false`: this link only picks, in every picker of it the app draws, not only where it is written |\n| `record` | list of alias (at least one) | no | The fields of the row this link names that this app saves from the link\'s card, which opens the row in the drawer: stored fields of the linked table a person writes. Stated once per linked table, and it holds on every card of that table in the app, where no List of the app draws its rows. Absent, the card reads the row |\n| `remove` | `true` | no | true: the row this link names is deleted from the link\'s card. Stated once per linked table, with its `record`, and it holds on every card of that table in the app. Absent, the card deletes nothing |\n\n<!-- generated:end node-field -->\n\nA field drawn in the head (title, subtitle, picture, figure, stage) is never also a Field node. A Field is drawn once per\npage. **Writes:** the field, under \xA7 App tree, and with `add` the target table\'s create.\n\n## Act node\n\nAn Act is a verb, stated whole where it stands. Its place decides what it works on:\n\n- in a Section, Side or Page, or in a one-part Record: one record, at that part\'s foot;\n- in a List: on each of its rows, offered in the row\'s \u22EF and, once the row is open, in its header\'s \u22EF; in a child List,\n the child\'s rows;\n- in a composed page\'s `Rows` (\xA7 Composed page): on each row of the child it lists, as in a child List;\n- in a List with `on`: the ticked rows (`"ticked"`) or every row in view (`"view"`), each checked before any is written;\n- directly in a Record that holds parts: refused.\n\n<!-- generated:start node-act -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The act\'s name; its workflow is `act_<key>`, or the `workflow` it names |\n| `label` | text | yes | The verb, as the reader says it |\n| `on` | `"ticked"` \\| `"view"` | no | In a List: the ticked rows, or every row in view; absent, one row |\n| `when` | [condition](#app-tree) | no | The act works only while each named select holds one of these options, each yes/no this value, each number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and each `{ "empty": true \\| false }` field is blank or filled |\n| `requires` | list of alias (at least one) | no | Fields that must be filled first, each drawn where the act stands; a blocked act names each one missing |\n| `recommends` | list of alias (at least one) | no | Fields the act runs without and reads when filled: while one is empty, the act says what it will leave blank |\n| `asks` | list of (alias \\| `{ "field": alias, "starts": "today" \\| "me" }` \\| [Asked input](#asked-input)) (at least one) | no | What the reader states when pressing it: fields of the row (written by the act) or inputs |\n| `set` | map of alias \u2192 [value](#app-tree) | no | What the act writes; each field is the act\'s, read-only elsewhere in the app |\n| `workflow` | alias | no | The workflow the act makes and runs under this name, for what `set` cannot say; no Workflow node is declared for it, and `set_app_workflow` sets its body |\n| `writes` | list of alias (at least one) | no | With `workflow`, fields of the acted row its body writes (an id kept from a service\'s answer) \u2014 the act\'s, as what `set` states is |\n| `template` | alias | no | A document template the act makes a file from: an html one made a PDF, an excel one a workbook, a word one a .docx; over several rows, one file of them all |\n| `templates` | list of [Act template](#act-template) (at least 2) | no | Several papers the act makes into `into`, instead of one `template`: the reader ticks which to make, one press making those |\n| `into` | alias | no | The record\'s files field the made document is kept in; remade, it replaces its own last paper, never a person\'s file |\n| `paper` | text | no | Its paper, `<child>.<kind>`: the line of the record\'s papers it stands on |\n| `intake` | alias | no | A child the record lists whose rows are its papers: the press takes papers, an agent reads them, and each is filed as a row of this child |\n| `fills` | list of text (at least one) | no | With `intake`, what the papers read may fill: a field of the record, `<link>.<field>` on the row a required one-row link names, or a child whose rows the papers add. With `record`, fields of the new row an agent fills from the transcript |\n| `record` | [Recording](#recording) | no | The press records a call, visit or meeting and files it as a new row of a child the record lists |\n| `sign` | [Signature](#signature) | no | The press opens a pad the signer signs on; the act keeps the signature as a picture with who signed, when and the witness |\n| `open` | text | no | The id of another app that reaches this row\'s table, opened at this row by the press: navigation only \u2014 no workflow runs, and a reader without `use` on it is shown that app\'s own refusal. Stated alone with `label` and `when` |\n| `review` | `true` | no | With `workflow`: the workflow only proposes a changeset of the act\'s table, and the reader ticks the rows to write |\n| `confirm` | `true` \\| text | no | The press asks first: the question, or `true` for the act\'s label over the row |\n| `danger` | `true` | no | The act destroys or cannot be undone |\n\n#### Asked input\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `input` | alias | yes | A value the act asks for that is not a field of the record |\n| `label` | text | yes | What the reader is asked, in their words |\n| `type` | `"text"` \\| `"long_text"` \\| `"number"` \\| `"date"` \\| `"select"` \\| `"link"` \\| `"member"` \\| `"file"` | yes | What the reader states. A `file` is one uploaded file, handed to the act\'s authored `workflow` as its file id and read by nothing else |\n| `options` | list of [Select option](#select-option) (at least one) | no | The choices a `select` input offers, one picked \u2014 written into a select holding the same option aliases |\n| `entity` | alias | no | The entity a `link` input picks one row of |\n| `accepts` | list of `"sheet"` \\| `"pdf"` \\| `"document"` \\| `"photo"` (at least one) | no | The kinds of file a `file` input takes; absent, a sheet, a PDF or a document \u2014 a phone offers its camera only where `photo` is among them |\n| `required` | `true` | no | The act is refused without it |\n| `default` | alias | no | A field of the row whose value the input starts from, still changed at will |\n| `add` | `true` \\| list of (alias \\| [Field](#field-node)) (at least one) | no | The new row this picker offers asks these fields; `true`: the target\'s required fields, its natural key and its title. Stated once per target table, and it holds in every picker of that table in the app |\n\n#### Set formula\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `formula` | text | yes | A formula over the acted row\'s fields, `{alias}` each, read as the entity\'s own formulas read them |\n\n#### Act template\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `template` | alias | yes | A document template the act makes a paper of, kept in `into` \u2014 an html one made a PDF, an excel one a workbook, a word one a .docx |\n| `when` | map of alias \u2192 (list of alias (at least one) \\| boolean) | no | The paper starts ticked while each named select of the record holds one of these options and each named yes/no (stored, or a formula \u2014 how a multi-select is tested: `includes({needs}, {needs:x})`) is this value \u2014 a suggestion the reader changes, never a refusal; absent, it starts ticked |\n\n#### Recording\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `into` | alias | yes | A child the record lists in a timeline or rows block, linking back to it by a single link: each recording is filed as a new row of it |\n| `audio` | alias | yes | The child\'s files field the recording\'s audio is kept in |\n| `transcript` | alias | yes | The child\'s text field the transcript is kept in, a speaker\'s turn per line |\n| `video` | alias | no | The child\'s files field the screen is kept in, where the recording captured it |\n| `at` | alias | no | The child\'s date field stamped with the moment the recording started |\n| `by` | alias | no | The child\'s member field set to the person who recorded |\n\n#### Signature\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `into` | alias | yes | The record\'s files field the signature is kept in, as a picture \u2014 each signing replaces the one before |\n| `by` | alias | yes | Who signed: a text field the signer types their name into (someone with no seat in the workspace \u2014 a driver, the owner\'s supervisor), or a member field set to the person pressing, who signs as themselves |\n| `at` | alias | yes | The record\'s date field stamped with the server\'s time as the act runs |\n| `witness` | alias | no | Beside a text `by`, the member field set to the person pressing, who saw the signer sign |\n\n<!-- generated:end node-act -->\n\nAn Act whose `when` does not hold is not offered. One whose `requires` are empty is offered disabled, naming each; one whose\n`recommends` are empty names what it will leave blank and still presses; one a standing Check blocks is offered disabled in\nthe check\'s words. The generated `act_<key>` workflow re-checks all three on the server, writes `set` and what was asked,\nand appends the status history where the act moves the status. A field an act asks is read-only everywhere else. A `select`\ninput offers its own options and is written into a select holding the same option aliases; a `link` input picks one row of\nits `entity`, a `member` input one person. A `file` input is one uploaded file handed to the act\'s authored `workflow` as its\nfile id; nothing else reads it. A field an act both `requires` and `asks` never disables the press.\n\n- **Writes:** every field it sets, asks, files into, signs or records is the act\'s.\n- **Papers.** A button that makes a paper is `template` (or `templates`) with `into` on the Act, with no workflow. `template` makes a document from the row as the act left it and keeps it in `into`; over several rows it\n makes one document of them all. `templates` names a set of papers kept in `into`, each made one as its file and each not\n made yet as a placeholder, those whose `when` holds ticked to start; the press makes the ticked ones, each replacing the\n file its template made before, and the set downloads as one archive (`archive_<key>`). `intake` reads papers: the press\n takes files, an agent reads them, and each is filed as the record\'s line of its kind in the named child; what they state\n of each field `fills` names is shown beside what the row holds, and the save writes only what differs. `sign` opens a pad\n on the device: the act keeps the picture in `into` in place of any before it, `at` stamped with the server\'s time and the\n person pressing in `witness`; the record reads `into` as one signature, and the act\'s paper is handed the picture under\n `into`\'s name (`{{#each signature}}<img src="{{url}}">{{/each}}` in an html template). `record` records a call or a visit\n through the host and files its audio, transcript and screen as a new row of a child.\n- **`workflow`** is an authored workflow (`workflows`) the act runs after its checks, for what neither `set` nor `template` can say.\n **`review: true`** makes it one that only proposes: its body reads, returns a changeset of the act\'s table in\n `data.review`, and the platform writes the rows the reader ticks in the act\'s dialog, each checked first and all written\n in one transaction or none. Over every row in view the act is handed no rows.\n- **A `when` that compares** splits one stage by an amount or a date, with no formula field behind it. The server refuses a\n press the row\'s numbers and dates do not meet, in the words "Approve works only while Amount is at most 5,000,000", and\n a blank number or date meets no bound. Two acts on an expense claim, the second moving it to a stage the Director group owns\n (stage owners decide who presses):\n\n```json\n{ "type": "Act", "key": "approve", "label": "Approve", "when": { "status": ["submitted"], "amount": { "lte": 5000000 } }, "set": { "status": "approved" } }\n```\n\n```json\n{ "type": "Act", "key": "send_to_director", "label": "Send to the director", "when": { "status": ["submitted"], "amount": { "gt": 5000000 } }, "set": { "status": "director_review" } }\n```\n\n```json\n{ "type": "Act", "key": "gate_in", "label": "Make the gate-in EIR", "template": "eir_in", "into": "eir", "sign": { "into": "driver_signature", "by": "driver_name", "at": "signed_at", "witness": "signed_before" } }\n```\n\n## Check node\n\nA Check is a yes/no or text formula of the rows of the List it stands in: it stands while true or non-empty, and a text\nformula\'s words are what the reader sees. Without `on` it only warns; with it, it refuses the saves and acts it names, on\nthe server, in every write it names.\n\n<!-- generated:start node-check -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A yes/no or text formula of the rows of the List it stands in: true or non-empty means the check stands |\n| `on` | `"all"` \\| list of (alias \\| `"update"` \\| `"delete"`) (at least one) | no | What the standing check refuses: `update` is the rows\' saves, an Act key is that act, `all` is every act and save; absent, it only warns. `delete` is this app\'s removal of the rows, for a rule no check of the table states |\n| `resolve` | alias | no | An Act on the same rows that clears it |\n\n<!-- generated:end node-check -->\n\n`update` is the rows\' saves; an Act key is that Act, on the same rows; `all` is every act and save.\nA rule that refuses a Delete is a check of the table, not a Check: state it in the table\'s `checks` with `on: ["delete"]`\n(\xA7 Write rules), where it holds for every way a row is deleted and a removal that cascades to child rows is refused whole.\nA Check naming `delete` refuses only this app\'s removal of a row, and is kept for a rule the table\'s checks cannot state (a\nformula that is neither a yes/no nor a text).\nA Check in a child List stands on each child row. A warning reading the figure of a meter that `gates` marks against its\nbound or its pass mark is refused: the meter already draws it.\n\nThe check field\'s `label` is its short name \u2014 "Over budget", "Release expired": a row\'s line says it in the check\'s tone\n(the most severe of several, and how many more), while its words whole are the row\'s tooltip. Label it as the reader names\nthe problem, in a few words, and let a text formula say the sentence. A check with no `resolve` links to the first field its formula reads that the record draws.\nA yes/no formula whose yes needs attention (papers the customer still owes) is a Check: its yes reads in the warning tone\nwherever it is drawn, every other yes in the success tone \u2014 and the row\'s line says it, so it is no column of the List too.\n\nA Check that every app over a table states the same way restates the table\'s contract and belongs in the table\'s `checks`\n(\xA7 Write rules).\n\nA Check refusing `update` (or `edit: false` on a record, part or field) makes the rows read-only in this app alone, drawn\nunder the read-only mark, never as a lock. A lock is the table\'s: a table workflow, a person, or an Act whose workflow calls\n`lock_records` locks a row, and every door refuses its writes until a table manager unlocks it \u2014 `unlock_records` in an\napp\'s run is judged as the member who pressed. A locked row reads locked in every app: its lock mark, its fields as values\nunder one line naming who can change it, its saves, Delete and each Act writing it refused \u2014 an Act that writes\nelsewhere, makes a paper it hands back, or unlocks it stays its workflow\'s to answer.\n\n## Reading node\n\nA Reading is a number over rows: one figure, its parts, a grid, a figure per period, steps; or a record\'s own figure. The\npicture is the runtime\'s, chosen from what the reading means \u2014 no prop picks a chart. The question is stated by which\nsplit it names:\n\n| Names | Answers |\n|---|---|\n| nothing | one figure: the rows counted, or `value` summed, `{ "avg": \u2026 }` averaged |\n| `by` | the figure per value of a select, one-row link, member or yes/no, or per band of a number\'s `tiers` |\n| `by` a date field | the figure per period (a trend); `ahead: true` reads forward over the rows still to come |\n| `by` and `across` | a grid, `by` down the side and `across` along the top |\n| `steps` | a funnel: 2 to 8 number fields summed, each kept as a share of the one before; in a record only |\n| no `entity`, in a Record | the record\'s own `value`, a tile under its field\'s label; two to eight consecutive tiles draw as one strip, and a single one is a field of the section |\n\n<!-- generated:start node-reading -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The reading\'s query name, unique in the app; a record\'s own figure is keyed by its field |\n| `label` | text | no | Absent, a split\'s `by` field\'s label, a trend\'s summed `value`\'s (else the rows\'), a grid\'s two fields\'; one figure states its label |\n| `entity` | alias | no | The List\'s rows; in a Record, the child\'s rows under it; absent in a Record with no `by`: the record\'s own `value` |\n| `via` | alias | no | The child\'s link to the record, where it has several |\n| `value` | alias \\| `{ "avg": alias }` | no | A number field summed over the rows, or `{ avg }` averaged; absent, the rows are counted |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, whose number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), whose field is `{ "empty": true \\| false }`, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `by` | alias \\| [Tiered filter](#tiered-filter) | no | Splits the rows into parts; a date field splits them per period |\n| `across` | alias \\| [Tiered filter](#tiered-filter) | no | With `by`: the columns of a grid |\n| `over` | alias | no | The date that places a row in time |\n| `ahead` | `true` | no | With a date `by`: the current period and those after it |\n| `steps` | list of alias (2\u20138) | no | Number fields summed, each a step of a funnel |\n| `on_hand` | [Reading on hand](#reading-on-hand) | no | With `by` (and `across`): the Reading\'s rows are what is owed, per value of those fields; drawn beside what is on hand per the same values, and the shortfall |\n| `target` | number | no | The number the figure is read against; absent, its bound, where the table states one |\n| `better` | `"up"` \\| `"down"` | no | Which way is good, for the figure\'s tone; absent, read from the figure\'s shape |\n| `tab` | alias | no | The Tab it reads, else the open one |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n\n#### Reading on hand\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `entity` | alias | yes | The entity whose rows are what is on hand, all of them |\n| `value` | alias | no | A number field summed over those rows; absent, the rows are counted |\n| `by` | alias | no | The field of this entity matching the Reading\'s `by`; absent, the same alias |\n| `across` | alias | no | The field of this entity matching the Reading\'s `across`; absent, the same alias |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, whose number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), whose field is `{ "empty": true \\| false }`, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n\n<!-- generated:end node-reading -->\n\nWhere a Reading stands decides which rows: a dashboard\'s every row, a List\'s the rows in view (so the search, the stage and\nthe filters narrow each one), a Record\'s the child\'s rows under the record. A Reading counts its rows, or sums the number\n`value` names \u2014 never a percent. `over` is a date, the row\'s own or its parent\'s through a lookup: a trend\'s periods, a\nfigure\'s recent periods, and what a dashboard\'s period windows. `where` keeps rows whose select holds one of the options, or\nwhose yes/no is the value, every entry ANDed \u2014 a band leads with the job\'s exception as a formula\'s yes/no (what is\nshort), never a stage the chips already count nor the overdue rows the register\'s summary line counts from `records.due`.\n\n- **A figure\'s change** is green where it moves the good way: a sum\'s rise, a count kept by `where` falling; `better` states\n the other way where that reads wrong (money going out). A value `limits` bounds reads against the sum of its bound\n (`10 / 14`); `target` reads one against a number nothing bounds.\n- **An average** is its sum over the rows that hold a value, divided by how many hold one; a row holding none is no part\n of it, and where none holds one the figure reads a dash, never 0. Under a dashboard\'s period it is the average over the\n period so far, never a sum of averages. A share is an average too: a number formula that is `100` on the rows that count\n and `0` on the others, in percent notation, averaged. Only a single figure averages.\n- **Bands.** `by` and `across` each take, in place of a field, a number by its `tiers` \u2014 `{ "field", "tiers" }` \u2014\n splitting the rows by the bands those breakpoints cut: `[1, 31, 61]` over days late reads not due, 1\u201330, 31\u201360 and over\n 60, the aged report. Every band is drawn in the bands\' own order, one holding no row at zero; rows holding no number\n stand apart. On a List a band is a door: a press narrows the rows to it. In a List\'s band, a split by one or two\n breakpoints stands in it, and one by three or more stands whole under it.\n- **In a List,** Readings stand under the title in one compact band: the job\'s headline number first, then at most the mix\n and the movement. A picture stands only where it splits the rows in view into two parts or more, one holding two rows or\n more. The summary line already totals the figure and the chips count the rows, so a figure summing the figure, or counting\n the rows of a table with a stage, is refused unless its `where` narrows it.\n- **Name.** A Reading\'s `key` is the query it is read by (`run_app_query`).\n\n## Progress node\n\nProgress reads one parent\'s work against its plan \u2014 a List\'s band over a folder, or a record\'s block, never a dashboard\'s\nevery row: the share of the work planned and the share done, cumulative day by day, today\'s two at its foot. Each row is a\npart of the work, its plan spread evenly over its planned days and weighing its `weight` (its value) or, absent, its planned\ndays. Its share done today is `done`, a percent the server computes; on an earlier day, its `done` from its `finished` day\non, before it what its `measured` rows add up to by then.\n\n<!-- generated:start node-progress -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The reading\'s query name, unique in the app |\n| `label` | text | yes | What the progress is of, in the reader\'s words |\n| `entity` | alias | yes | The rows that are the parts of the work: the List\'s, or in a Record the child\'s rows under it |\n| `via` | alias | no | The child\'s link to the record, where it has several |\n| `planned` | [Progress planned](#progress-planned) | yes | The days each row\'s work is planned over, its share planned spread evenly across them \u2014 a row missing either is no part of the plan, and neither line counts it |\n| `done` | alias | yes | A percent field of the row the server computes: its share done today (0\u2013100) |\n| `finished` | alias | no | A date of the row: the day its work was finished, from which it stands at its `done` |\n| `weight` | alias | no | A number of the row: what it weighs in the whole; a row holding none weighs nothing. Absent, each row weighs its planned days |\n| `measured` | [Progress measured](#progress-measured) | no | Before a row is finished, its share done on a day is what these add up to by then, never past its `done`; absent, a row stands at nothing until it is finished, and at its `done` today |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, whose number or date meets its bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is read when the rows are), whose field is `{ "empty": true \\| false }`, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `tab` | alias | no | The Tab it reads, else the open one |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n\n#### Progress planned\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `start` | alias | yes | A date of the row: the first day of its planned work |\n| `end` | alias | yes | A date of the row: the last day of its planned work |\n\n#### Progress measured\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `rows` | alias | yes | The entity whose rows record the work as it is done, each through its one-row link to a row of the reading |\n| `on` | alias | yes | A date of those rows (their own, or a lookup): the day each records its work |\n| `share` | alias | yes | A number of those rows the server computes: the share of its row each adds, in percent |\n\n<!-- generated:end node-progress -->\n\n## Statement node\n\nA Statement draws a figure the record works out from its own figures \u2014 a claim\'s payable, an invoice\'s total, a payslip\'s\nnet \u2014 line by line in the order the arithmetic runs: each line a number of the record, the first stating no `op`, the last\nan `equals`. Every figure is a field the server computes or a person writes; the screen adds nothing up. A line\'s `parts`\nlists the rows its figure carries, each read from its own row, and that figure is what they add up to \u2014 a formula, rollup\nor lookup, or the server\'s copy when the record is made (`default_from`), which nothing writes after.\n\n<!-- generated:start node-statement -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | The statement\'s name, unique among its siblings |\n| `label` | text | no | Absent, the label of what the statement comes to |\n| `lines` | list of [Statement line](#statement-line) (at least 3) | yes | The figures it is built from, top to bottom, ending in the one it comes to |\n| `when` | [condition](#app-tree) | no | Drawn only while the record\'s selects hold one of these options, its yes/nos this value, its numbers and dates meet their bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }` is today) and its `{ "empty": true \\| false }` fields are blank or filled |\n\n#### Statement line\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `field` | alias | yes | A number of the record \u2014 stored, a formula, a rollup or a lookup \u2014 read on the line after its label |\n| `op` | `"plus"` \\| `"minus"` \\| `"times"` \\| `"equals"` | no | How the line works on the lines above it \u2014 `plus`, `minus`, `times`, or `equals` for what they come to, a formula, rollup or lookup the server computes; absent on the first line, which the rest work from |\n| `rate` | alias | no | A percent of the record read after the line\'s label: the rate its figure is taken at (retention at 5 %) |\n| `parts` | [Statement parts](#statement-parts) | no | The rows the line\'s figure carries, each listed under it with its words and its own figure \u2014 read from those rows, never a copy of them. The line\'s figure is what they add up to, drawn read: a formula, rollup or lookup, or a value the server copies when the record is made (`default_from`) that nothing writes after |\n\n#### Statement parts\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `rows` | alias | yes | The entity whose rows the line\'s figure carries \u2014 the record\'s own for the earlier rows of the same thing |\n| `same` | alias | yes | A one-link the record and those rows both hold: the rows naming what the record names in it |\n| `at` | alias | no | A moment (a datetime) of those rows no person keys \u2014 when each was certified: with `before`, only the rows whose moment stands before the record\'s, earliest first; absent with it, every row so named, in their own order |\n| `before` | alias | no | A moment (a datetime) of the record no person keys \u2014 when it was made: the moment the rows\' `at` stands before; stated with `at` |\n| `where` | map of alias \u2192 (list of alias (at least one) \\| boolean \\| [comparison](#values) \\| `{ "empty": true \\| false }`) | no | Only the rows whose single select holds one of these options, whose yes/no (stored, or a formula) is this value, whose number or date meets its bounds, or whose field is empty or filled \u2014 read so on the server |\n| `columns` | list of alias (1\u20132) | yes | The rows\' fields read as each one\'s words, in order (its date, its percent) |\n| `figure` | alias | yes | The rows\' number each one reads at the figures\' edge, frozen when the row was certified: a stored number an act fills (`{ "formula": \u2026 }` as it runs) or the server copies when the row is made (`default_from`) \u2014 never one a person keys, nor a formula, rollup or lookup the server recomputes |\n\n<!-- generated:end node-statement -->\n\n```json\n{\n "type": "Statement",\n "key": "claim",\n "lines": [\n { "field": "contract_value" },\n { "field": "done_to_date", "op": "times" },\n { "field": "value_to_date", "op": "equals" },\n { "field": "certified_before", "op": "minus", "parts": { "rows": "claim", "same": "contract", "at": "certified_at", "before": "made_at", "where": { "kind": ["work"] }, "columns": ["claimed_on", "done_to_date"], "figure": "certified_value" } },\n { "field": "claim_value", "op": "equals" },\n { "field": "retention", "op": "minus", "rate": "retention_rate" },\n { "field": "payable", "op": "equals" }\n ],\n "when": { "kind": ["work"] }\n}\n```\n\n## Export node\n\nAn Export is a report file of the rows in view: the title, a line per chip, the search and the moment it was exported, the\nReadings, and every row, at most 20,000. With no props, the one Export is the workbook of the List\'s columns, the Readings\non a sheet before the rows. A List states the reports the business sends \u2014 one a button, several one menu \u2014 each a `label`,\na `description`, and a `template` filled with `title`, `lines`, `readings`, `at` (the moment it was made), `dates` (each\ndate filter\'s `from` and `to` by field) and the rows under the table\'s alias (an html one made a PDF, an excel one a\nworkbook). A report\'s `filename` names its file in the template grammar over the same keys but the rows. Where `per` is a\none-row link, `per_row.<field>` reads a text, number, date or yes/no field of the picked row, and `| add_days:<n>` shifts a\ndate by whole days (`n` a number or such a field, empty for none): `Stock {{per}} {{period.to | add_days:per_row.day_offset | format:"dd.MM.yyyy"}}`. A report of some\nrows (`where`), or of one value the reader picks (`per`: one file per value), is made of the List as those chips would\nnarrow it; the screen keeps the chips and rows it shows.\n\n<!-- generated:start node-export -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `key` | alias | yes | Unique among its siblings; part of the node\'s address |\n| `label` | text | no | The report\'s name; absent on the one Export that stands for the register\'s workbook |\n| `description` | text | no | What the report is for, in a sentence |\n| `template` | alias | no | An html or excel document template the report fills, with `title`, `lines`, `readings`, `at`, `dates`, `period`, `per` and `per_row` (a one-row `per`\'s picked row, the fields `filename` reads); absent, the workbook of the List\'s columns |\n| `filename` | text | no | The file\'s name, at most 500 characters, in the template grammar, over one value each: `title`, `at`, `dates.<field>.from` or `.to`, `period.from` or `.to`, `per`, `per_row.<field>` (a text, number, date or yes/no field of the row a one-row `per` picks), `lines \\| lookup:<n>`; `\\| add_days:<n>` shifts a date by whole days, `n` a number or `per_row.<field>`. Absent, the report\'s label |\n| `where` | [narrowing](#app-tree) | no | Only the rows whose select holds one of these options, whose yes/no is this value, or whose member field holds the reader (`{ "me": true }`) \u2014 every entry ANDed |\n| `per` | alias | no | A select, member or one-row link the reader picks one value of: one file of that value |\n| `books` | list of alias (at least one) | no | Of the List\'s books, the tables the report reads; absent, all |\n\n<!-- generated:end node-export -->\n\n## Book node\n\nA Book is another table read as part of the register: the same job over its rows \u2014 a legacy ledger, a second branch\'s book.\nA row of the book opens the List\'s Record, lined up by alias; a field the book lacks is blank on its rows and drops from\nwhat they open, and an Act, Check or add needing it is not offered on them. Options line up by alias. The Book\'s own Acts\nand Checks, in its own fields, stand beside the List\'s, each Act\'s `key` its own among the App\'s: an Act of the List running\nits own `workflow`, papers or a call never runs on a book\'s rows, so the Book states its own. With `column: true` the List\ndraws a column naming the table each row is of, and a filter by it. A Book holds the title, the stage, the scope and every\nCheck locking the rows; a table, cards or a plan reads Books.\n\n<!-- generated:start node-book -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `entity` | alias | yes | Another table read as part of the register: the same job over its rows |\n| `fields` | map of alias \u2192 alias | no | A field of the List\'s entity \u2192 the field of this entity holding the same fact, where their aliases differ |\n| `column` | `true` | no | The List draws a column naming the table each row is of, and a filter by it |\n| `row` | [Book row](#book-row) | no | The book\'s rows, stated where they differ from the List\'s |\n| `children` | list of ([Record](#record-node) \\| [Act](#act-node) \\| [Check](#check-node)) (at least one) | no | The Fields a save of the book\'s rows writes (and `edit: false` for none), in a Record as a child List\'s Record states them \u2014 the page a row opens is the List\'s; and the book\'s own Acts and Checks over its rows, beside the List\'s its rows can run |\n\n#### Book row\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `status` | `{ "edit": true }` | no | `{ edit: true }`: the book\'s stage is also moved by hand |\n\n<!-- generated:end node-book -->\n\n**Writes:** the List\'s `remove`, its Fields and its Acts, on the book\'s own table.\n\n## Query node\n\nA Query is a read for agents, never drawn. An app generates a query for everything its screen reads (`agent_surface` lists them); a Query declares one the screen never asks, called through `run_app_query` or the app\'s API: the\ntable whose rows it reads, the fields each row returns or the figure they add up to, and what narrows them.\n\n<!-- generated:start node-query -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `description` | text | yes | What the query answers, read by whoever calls it by its alias \u2014 at most 300 characters |\n| `params` | map of alias \u2192 [Workflow value](#workflow-value) | no | What a call states, by name, each read by an entry of `where` \u2014 a `number`, `boolean`, `date`, `select`, `record_link`, `member`, holding one. `required: false`: the call may leave it out, and the condition reading it narrows nothing |\n| `rows` | alias | yes | The entity whose rows it reads, under that entity\'s read scope. The app\'s own entity is read as its list reads it: kept by the register\'s `where`, and inside the row the app\'s `scope` names, which the call states |\n| `fields` | list of alias (at least one) | no | The fields each row returns, by alias. A query states `fields` or `reading`, one of the two |\n| `sort` | [Sort key](#sort-key) \\| list of [Sort key](#sort-key) (at least one) | no | The order the rows return in; absent, the order the entity\'s list reads in. Only with `fields` |\n| `limit` | integer | no | The most rows returned. Only with `fields` |\n| `reading` | [Query reading](#query-reading) | no | The rows counted or summed instead of returned: one row for all of them, or one per value of `by` or per day of `over`, each carrying its `by` field or `__day`, `__count`, and `__sum` where `value` is stated |\n| `key` | alias | yes | The query\'s name, what `run_app_query` calls; unique in the app |\n| `where` | map of alias \u2192 [condition](#query-node) | no | What narrows the rows, as a List\'s `where` does: a select\'s options, a yes/no\'s value, a number or date meeting its bounds (`gt`, `gte`, `lt`, `lte`), a field `{ "empty": true \\| false }`, `{ "me": true }` on a member field \u2014 or a value the call states (`{ "param": name }`), or a date or number range of params (`{ "from": \u2026, "to": \u2026 }`) |\n\n#### Query reading\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `value` | alias | no | A number field summed over the rows; absent, the rows are counted |\n| `average` | `true` | no | The `value` is averaged: summed over the rows holding one, beside how many hold one. Never with `by` or `over` |\n| `by` | alias | no | The single select, one-row link, member or yes/no each row is counted under \u2014 one row returned per value |\n| `over` | alias | no | A date: one row returned per day of it. Never with `by` |\n\n#### Query param\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `param` | text | yes | A param of this query: the condition holds what the call states for it, and is left out where the call states none |\n\n#### Query range\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `from` | [Query param](#query-param) | no | The param the field is on or after \u2014 a date\'s first day, a number\'s least |\n| `to` | [Query param](#query-param) | no | The param the field is on or before \u2014 a date\'s last day, a number\'s most |\n\n<!-- generated:end node-query -->\n\n```json\n{\n "type": "Query",\n "key": "fees_unbilled",\n "description": "A customer\'s fees no invoice covers yet, oldest first.",\n "params": {\n "customer": { "type": "record_link", "entity": "customer", "description": "The customer whose fees are read." },\n "since": { "type": "date", "required": false, "description": "Only fees charged on or after this day." }\n },\n "rows": "fee",\n "fields": ["visit", "service", "amount", "charged_on"],\n "where": { "billed": false, "customer": { "param": "customer" }, "charged_on": { "from": { "param": "since" } } },\n "sort": { "field": "charged_on" },\n "limit": 200\n}\n```\n\nAn entry of `where` holds what a Reading\'s `where` holds, or a value the call states: `{ "param": "<name>" }`. A required\nparam is always stated; one stating `required: false` may be left out, and the condition reading it then narrows nothing.\nA query reads under its table\'s read scope, as every read of the app does. A `reading` returns one row for all the rows it\nreads, or one per value of `by` or per day of `over`. `set_app` compiles a Query with the rest of the app: no code is set.\n\nEvery Reading of the app is a query too, returning its groups, called by its key. A Tab\'s `totals` read is called by\n`<table>_totals_<tab>` with the period\'s two days, `from` and `to`, both required.\n\nA query reads one table: on an app that states `Book`s, whose list reads several, it names a book\'s own table, never the\nList\'s. A param compares a field holding one value \u2014 or, as a range, a date or a number \u2014 and never a number read in each\nrow\'s own unit or currency, nor a date holding a span.\n\n## Workflow node\n\nA Workflow is a verb for agents, never drawn: a job no press of the screen starts \u2014 an agent or a service handing the app a\nfile, or a list of rows to reconcile a table against. It states what it takes and what it hands back; a table is named by\nits alias in `tables` and a select by `<table>.<field>`. It is called through the app, by `run_app_workflow` or the app\'s\nAPI. On an app shared by a public link it runs only for someone who holds the app, and a visitor\'s call is refused as an\nalias the app does not have.\n\n<!-- generated:start node-workflow -->\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `description` | text | yes | What the workflow does, read by whoever calls it by its alias \u2014 at most 300 characters |\n| `inputs` | map of text \u2192 [Workflow value](#workflow-value) | no | What a call hands it, by name \u2014 checked before its body runs |\n| `outputs` | map of text \u2192 [Workflow value](#workflow-value) | no | What its body returns as `data`, by name \u2014 checked as it returns; absent, read off the body |\n| `key` | alias | yes | The workflow\'s name, what `run_app_workflow` calls; unique in the app |\n\n#### Workflow value\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `type` | `"text"` \\| `"number"` \\| `"boolean"` \\| `"date"` \\| `"datetime"` \\| `"email"` \\| `"json"` \\| `"record_link"` \\| `"select"` \\| `"member"` \\| `"file"` \\| `"object"` \\| `"array"` | yes | What the value is |\n| `description` | text | no | What the value is for, read by whoever calls the workflow |\n| `required` | `false` | no | false: the value may be left out; absent, it is always there |\n| `multi` | `true` | no | A `record_link`, `select`, `member` or `file` holding several |\n| `entity` | alias | no | The entity a `record_link` names a row of |\n| `field` | text | no | `<entity>.<field>`: the select field whose options a `select` takes, as the field holds them when the workflow runs |\n| `options` | list of alias (at least one) | no | The values a `select` takes, where no field holds them |\n| `fields` | map of text \u2192 [Workflow value](#workflow-value) | no | An `object`\'s values, by name |\n| `items` | [Workflow value](#workflow-value) | no | What each item of an `array` is |\n\n<!-- generated:end node-workflow -->\n\n```json\n{\n "type": "Workflow",\n "key": "reconcile_fees",\n "description": "Reconciles a visit\'s fees against the lines of the carrier\'s statement.",\n "inputs": {\n "visit": { "type": "record_link", "entity": "visit" },\n "statement": { "type": "file", "required": false },\n "lines": { "type": "array", "items": { "type": "object", "fields": { "service": { "type": "select", "field": "fee.service" }, "amount": { "type": "number" } } } }\n },\n "outputs": { "added": { "type": "number" }, "changed": { "type": "number" } }\n}\n```\n\nIts code is set on the app, never stated in the tree: `set_app_workflow` with the app, the key and the `source`, leaving\n`inputs` and `outputs` out \u2014 the code is checked against what the node declares. Every `set_app` keeps that code and holds\nwhat it takes, what it hands back and its description to the node. To change what a workflow takes or hands back, change\nthe node first: `set_app` keeps the code where it still meets the change, and where it does not the workflow stands on no\ncode again until `set_app_workflow` sets one that does. With no code it refuses every call, and each `set_app` says so. A\nWorkflow the tree stops declaring is archived with its key.\n\n## Composed page\n\nA Record whose page is its own arrangement of what the app already states \u2014 a piece of writing under its title, a brief\nbeside its figures \u2014 states `compose` instead of parts: a tree of finished pieces, each named by `use`. A `Rows` holds\nthe Acts and Checks of its rows as `children`, each named by `type`: they are the app\'s own acts and checks on those rows,\nnever pieces of the page.\n\n```json\n{\n "type": "Record",\n "door": "page",\n "compose": {\n "use": "Column",\n "children": [\n { "use": "Bar", "children": [{ "use": "Status" }, { "use": "Field", "field": "audience" }, { "use": "Act", "act": "send_for_review" }, { "use": "Copy", "field": "body", "as": "plain" }] },\n { "use": "Title" },\n { "use": "Document", "field": "body" },\n { "use": "Group", "title": "Reviews", "children": [{ "use": "Rows", "child": "review", "create": false, "children": [{ "type": "Act", "key": "resolve_review", "label": "Resolve", "workflow": "resolve_review", "confirm": true }] }, { "use": "Act", "act": "ask_reviewer" }] }\n ]\n }\n}\n```\n\nA node binds by name: a `field` is a field of the record\'s table, an `act` an Act of the app, a `child` a table linking to\nthe record. What a name binds is read and written exactly as a part naming it would be \u2014 a `Field` and a `Document` are\nsaved through the record\'s own save, an `Act` runs its workflow, a `Rows` reads the child\'s rows \u2014 so a piece adds no write\nof its own; only an Act inside a `Rows` declares one, as an Act of a child List does. A piece holds no condition and no computed value: a piece that does not apply on a row draws nothing, and anything\nthat must depend on the row is an Act\'s `when` or a field\'s formula, never the tree.\n\nThe first `Act` the page itself draws is its primary and every other one secondary; a `danger` act reads so wherever it\nstands. A `Copy` and a `Document` naming one field copy what is on screen \u2014 the words typed and not yet saved among them. The\nrecord\'s chrome stays the runtime\'s: the way back, its comments door and its \u22EF with Delete share the row over the tree, and the\nhead draws no title and no status of its own \u2014 the tree\'s `Title` and `Status` do.\n\n<!-- generated:start apps-compose -->\n\n#### Column piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Column"` | yes | The page\'s reading column, 680px: its pieces stacked in order from one left edge |\n| `children` | list of [piece](#composed-page) (at least one) | yes | The pieces stacked in it, in the order they are read |\n\n#### Bar piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Bar"` | yes | One row of small things on one control height, in two zones: what the record is \u2014 a status, a field\'s control \u2014 from its left edge, what is done to it \u2014 an act, a copy \u2014 ending on its right edge, taking a row of their own under it where the row runs out of room |\n| `children` | list of [piece](#composed-page) (at least one) | yes | Its pieces, each zone\'s in order; a `Field` directly in it is its control alone on one line, named by its label where it holds nothing; an `Act` that destroys or corrects is not drawn here \u2014 it stands in the record\'s \u22EF |\n\n#### Group piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Group"` | yes | A titled stack, headed once by `title` |\n| `title` | text | yes | Its heading, in the reader\'s words; holding one `Rows`, it carries their count and their add, and the group\'s `Act`s end its line |\n| `children` | list of [piece](#composed-page) (at least one) | yes | The pieces under the heading, in order |\n\n#### Panel piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Panel"` | yes | What a reader does not need while writing: nothing in the page, one entry of the record\'s \u22EF named by `title` that opens its pieces in a panel beside the page \u2014 a sheet on a phone; directly in the root `Column` alone |\n| `title` | text | yes | Its entry in the \u22EF and the panel\'s name, in the reader\'s words; an `Act` directly in it ends that name\'s row |\n| `children` | list of [piece](#composed-page) (at least one) | yes | The pieces in the panel, in order: `Rows`, `Field`, `Act`, `Group`, `Document` \u2014 never `Title`, `Status`, `Bar`, `Copy` or another `Panel` |\n\n#### Title piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Title"` | yes | The record\'s title (`records.<entity>.title`) as the page\'s heading, written in place where a person writes it; one a page |\n\n#### Status piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Status"` | yes | The record\'s status (`records.<entity>.status`) as a record\'s head draws it: moved by hand, by its acts\' moves, or read |\n\n#### Field piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Field"` | yes | A field of the record under its label, as a section\'s `fields` draws it |\n| `field` | alias | yes | The field drawn \u2014 never the title or the status, which `Title` and `Status` draw; once a page |\n\n#### Document piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Document"` | yes | A markdown text field written in place with no box, saved as it is written \u2014 whether it is saved said at its foot |\n| `field` | alias | yes | A text field with `format: "markdown"`; once a page |\n\n#### Act piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Act"` | yes | An act of the app on the record as its button: its `when`, what it lacks, its asks and its confirmation as everywhere; the first `Act` the page itself draws is its primary |\n| `act` | alias | yes | An act of this app on the record \u2014 never one `of` a child\'s rows, which stands on its row; once a page |\n\n#### Rows piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Rows"` | yes | A child entity\'s rows under the record, as a rows block lists them, each row\'s own acts on it |\n| `child` | alias | yes | A child entity linking to the record; once a page |\n| `create` | `false` | no | false, rows are not added here \u2014 an act of the page makes them |\n| `remove` | `true` | no | The rows offer a Delete (open row and \u22EF); absent, none |\n| `children` | list of ([Act](#act-node) \\| [Check](#check-node)) (at least one) | no | The acts and checks of its rows, each standing on every row listed, as a child List\'s stand on its rows; absent, the rows offer none |\n\n#### Copy piece\n\n| Key | Type | Required | Meaning |\n|---|---|---|---|\n| `use` | `"Copy"` | yes | A button copying a field\'s words as the page holds them now \u2014 what is typed in its `Document` and not yet saved among them |\n| `field` | alias | yes | The text field copied |\n| `as` | `"plain"` \\| `"raw"` | yes | `plain`, its markdown marks gone; `raw`, as stored |\n\n<!-- generated:end apps-compose -->\n\n## Tasks\n\nA table whose `records` states `"task": true` \u2014 its status a stored select with `closed` \u2014 is work someone finishes.\nEvery List of its rows draws it alike, each row led by a ring. Ticking the ring moves the row to a closed option by the\none write the app already has for that move: the Act of that table setting a closed option, offered on an open row and\nasking nothing, else its own save where people write the status \u2014 stamping what that act stamps and appending the\nstatus history. Unticking runs the Act moving a closed row back, else the save back to the status\'s default. A move no\nwrite reaches leaves the ring disabled and the check notes it. An Act with `on: "ticked"` completes several at once.\n\nA record listing task rows filed under it by a link holding one row says how much of that work is done \u2014 those at a\nclosed option of all \u2014 wherever its row is drawn: a meter on its head, a column of its register\'s rows, a card\'s foot, a\ncalendar entry, a gantt\'s or a lane\'s bar. No rollup is stated for it.\n\n```json\n{\n "tables": [\n {\n "ref": "project",\n "label": "Projects",\n "singular": "Project",\n "fields": {\n "name": { "label": "Name", "required": true, "type": "text" },\n "lead": { "label": "Lead", "type": "member" }\n },\n "title": "name",\n "records": {}\n },\n {\n "ref": "task",\n "label": "Tasks",\n "singular": "Task",\n "fields": {\n "title": { "label": "Title", "required": true, "type": "text" },\n "project": { "label": "Project", "required": true, "type": "link", "table": "project", "multi": false },\n "assignee": { "label": "Assignee", "type": "member" },\n "status": {\n "label": "Status",\n "required": true,\n "default": ["todo"],\n "type": "select",\n "options": [\n { "alias": "todo", "label": "To do", "color": "slate" },\n { "alias": "done", "label": "Done", "color": "green" }\n ]\n },\n "due": { "label": "Due", "type": "date", "format": "date" },\n "done_on": { "label": "Done on", "type": "date", "format": "date" }\n },\n "title": "title",\n "records": {\n "subtitle": ["assignee"],\n "due": ["due"],\n "task": true,\n "starts": { "assignee": "me" },\n "status": { "field": "status", "closed": ["done"] }\n }\n }\n ]\n}\n```\n\n```json\n{\n "body": {\n "type": "App",\n "key": "projects",\n "name": "Projects",\n "color": "blue",\n "tables": { "project": "<id of the project table>", "task": "<id of the task table>" },\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "project",\n "children": [\n {\n "type": "Record",\n "children": [\n {\n "type": "Section",\n "key": "work",\n "title": "Work",\n "children": [\n "lead",\n {\n "type": "List",\n "entity": "task",\n "columns": ["due"],\n "children": [\n { "type": "Act", "key": "complete", "label": "Complete", "when": { "status": ["todo"] }, "set": { "status": "done", "done_on": { "now": true } } },\n { "type": "Act", "key": "reopen", "label": "Reopen", "when": { "status": ["done"] }, "set": { "status": "todo", "done_on": null } },\n { "type": "Act", "key": "complete_all", "label": "Complete", "on": "ticked", "when": { "status": ["todo"] }, "set": { "status": "done", "done_on": { "now": true } } }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\nThe tasks as a register of their own, for one person: a one-part Record holds its Fields and Acts directly.\n\n```json\n{\n "body": {\n "type": "App",\n "key": "my_tasks",\n "name": "My tasks",\n "color": "teal",\n "tables": { "project": "<id of the project table>", "task": "<id of the task table>" },\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "task",\n "opens": { "assignee": { "me": true } },\n "children": [\n { "type": "Reading", "key": "open_tasks", "label": "Open", "where": { "status": ["todo"] } },\n {\n "type": "Record",\n "door": "drawer",\n "children": [\n "project",\n "due",\n { "type": "Act", "key": "finish", "label": "Finish", "when": { "status": ["todo"] }, "set": { "status": "done", "done_on": { "now": true } } },\n { "type": "Act", "key": "undo", "label": "Undo", "when": { "status": ["done"] }, "set": { "status": "todo", "done_on": null } }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## App rules\n\nEvery rule the check enforces over an app, one sentence each under an id; a finding names its rule\'s id\n(`[register.filter]`), and `model/<rule id>` is that one rule\'s page. A sentence names the node and the prop an author writes (`List.create`, a `Section`);\neach area stands in the tree where this table says:\n\n| Area | Stands at |\n|---|---|\n| `app` | the App, and the register List\'s props that belong to the app: `words` (labels), `color`, `scope`, Books, logs |\n| `register` | the register List: its columns, filters, search, group, sort, layout, tabs, period, export |\n| `reading` | a Reading or Progress |\n| `record` | a Record and its parts: Section, Side, Page, Act placement |\n| `block` | what a part holds: a child List, a Field gallery or prose block, a Reading, a desk, a timeline or agenda |\n| `statement` | a Statement |\n| `act` | an Act |\n| `check` | a Check |\n\n<!-- generated:start rules-app -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `app.alias-unique` | An App\'s `key` is unique among apps, and an Act\'s among the Acts of its App, every List\'s included. |\n| `app.icon` | An App\'s `icon` is a Lucide icon name in kebab-case, as `receipt` or `trending-up` are. |\n| `app.add-label` | A List\'s `add_label` stands only where it adds rows (`create`), in words that are not empty and hold no `{` or `}`. |\n| `app.labels` | An App\'s `words` name a table it reads and a field or option of it, in words that are not empty and hold no `{` or `}`; no two fields of a table, nor two options of a field, show the same words. |\n| `app.records-entry` | Every table an App draws a List over, opens or picks from \u2014 the register\'s, a child List\'s, a linked row\'s, a documents List\'s \u2014 has a `records` entry. |\n| `app.scope` | The App\'s `scope` is a required one-row link of the first List\'s entity, the same on every row the App lists \u2014 never a `columns`, `filters` or `search` entry, `group`, `where`, `opens`, a Field of a part or a `create` question. |\n| `app.when` | A node\'s `when` holds single selects to options, yes/nos to `true` or `false` (stored or a formula of one; a formula with `options` is a select), numbers and dates to bounds (`gt`, `gte`, `lt`, `lte`; a date\'s bound is a day, a moment on a datetime, or `{ "now": true }`) and any field but a yes/no to `{ "empty": true \\| false }` \u2014 a number read in each row\'s own unit or currency and a lookup take no bound. |\n| `app.asked-written` | What a List\'s `create` asks, what an Act\'s `asks` asks and what `starts` fills is a field a person writes \u2014 never a formula, rollup, lookup, autonumber or a date the workspace stamps. |\n| `app.own-rows` | A many-link to rows that each belong to one row of this entity is never a Field of a part, a `create` question or an Act\'s `asks`: those rows are the record\'s own, read as a child List. |\n| `app.create-asks` | A List\'s `create` asks fields a person writes \u2014 never the link or the folder it files the row under, which the add sets itself. |\n| `app.create-required` | A List\'s `create` asks every field its entity requires that nothing else fills \u2014 a `default`, `starts`, the record or folder it files the row under, or a `default_from` over a link it fills \u2014 or the server refuses every add. |\n| `app.books` | A register App\'s Books each name another entity once (`entity`) \u2014 never the App\'s own \u2014 whose fields line up with the register List\'s by alias, else by `fields` (a field the List reads, or one an Export prints \u2192 one of the Book\'s): read as one column type (a day apart from a moment), one value or several alike, a link to the same entity. Each Book holds the title, the status where the List shows one, the folder (`scope`), what `where` keeps and every Check locking the rows; the List is a table, cards or a plan, whose `at`, `across` and `up` line up like any field. Two Books holding a field of their own under one alias hold it as one kind of value. An Export\'s `books` each name the List\'s entity or one of its Books, once, and one of them holds each field its `where` and `per` narrow by. A Book\'s `column: true` draws the column naming each row\'s table in a List that has Books. A Book\'s own Acts and Checks, over its rows in its own fields, stand beside the List\'s: each Act\'s `key` its own among the App\'s Acts, its Books\' and its logs\', and its `workflow` run by no Act of another List or Book. |\n| `app.book-option-kin` | *Noted, never refused:* A Book\'s option labelled as one of the App\'s, under another alias, reads apart from it: the List lists both and counts each on its own. |\n| `app.book-lacks` | *Noted, never refused:* A Book lacking a field the Record, an Act or a `create` reads: what reads it drops from the Book\'s rows \u2014 an Act, a child List, a Section, the `create`. An Act of the List running its own `workflow`, reading papers or recording a call, and an Act of a child the Book\'s record lists no rows of: none is offered on the Book\'s rows, which run the Book\'s own Acts. A field only an Export prints, held by a Book as another kind of value, prints blank on its rows. A Tab\'s `totals` over a child linking to no row of a Book total nothing on its rows. |\n| `app.logs` | A register App\'s logs \u2014 further Lists, at most 3 \u2014 each list another entity under a `key` of their own: never the App\'s own entity, one of its Books\' or another log\'s. A log is one List \u2014 a table or cards, with no Tabs, no Book `column` and no composed Record \u2014 over an entity whose status states no owned `stages`, and stands in an App that states no `scope`. An Act\'s `key` is its own among the App\'s Acts, its logs\' included. No Record of the App or of a log lists the rows of an entity another of the App\'s Lists is over: a link to one opens it on its own List. A List people work as a desk of its own is another App. |\n| `app.row` | A List\'s `row` states only what differs from `records`: a `subtitle` that is not the title, a number for its `figure`, dates for its `due`, an `image` that is a files picture no Act makes documents into, or `false` for a figure, deadlines or picture the entity states and the App draws none of. |\n| `app.tasks` | *Noted, never refused:* A task entity an App lists whose rows no write of the App moves to a closed option (its ring stands disabled), or that an Act ticks done and none moves back. |\n| `app.workflow-value` | A value a Workflow takes or hands back states what its type needs and nothing another type does: a `record_link` its `entity`, a `select` its `options` or the `field` holding them \u2014 never both \u2014 an `object` its `fields`, an `array` its `items`; `multi` stands on a `record_link`, `select`, `member` or `file`; an output is never a `member` or a `file`; and a value nests at most 8 deep. |\n| `app.query` | A Query reads one entity\'s rows as `fields` or as a `reading`, one of the two \u2014 never the App\'s own entity where the App has Books, whose List reads several tables; `sort` and `limit` stand with `fields`; a `reading` splits by `by` or by the day of `over`, never both, and averages only with neither; each param is a number, a yes/no, a date, a select, a one-row link or a member holding one, read by an entry of `where` on a field of its type \u2014 a date by a range, a number by either, and never a number read in each row\'s own unit or currency or a date holding a span. |\n| `app.generated-name` | A name the App generates is taken by nothing the App states. No Query, no Reading\'s or Progress\'s `key` \u2014 a log\'s as it is read, `<entity>_<key>` \u2014 and no Tab\'s read (`<entity>_list_<tab>`) is the name of a read the App generates, of a Query or of a Reading. |\n| `app.surface-described` | *Noted, never refused:* A value a Workflow takes, and a param a Query takes, that states no `description`: the one line a caller reads to fill it. |\n| `app.keys-unique` | No two children of one node share a key: an address names one node. |\n| `app.states-list` | A register App states the List whose rows it lists; an App stating none is a dashboard and holds Readings. |\n| `app.period` | An App\'s `period` is a dashboard\'s: a register states its own on its List. |\n| `app.dashboard-words` | A dashboard reads every table\'s rows whole and states no `words` or `scope`: those are a register App\'s. |\n| `app.add-once` | A table\'s add by a picker is stated once \u2014 at one Field\'s `add` or one link input\'s `add` \u2014 and every picker of that table adds by it. |\n| `app.card-once` | A table\'s card (`record`, `remove` on a link Field) is stated once, at one Field. |\n| `app.card-fields` | A card\'s `record` names, once each, fields of the linked table its save may change. |\n| `app.picker-link` | `add`, `record` and `remove` stand on a Field that is a link: only a link has a picker and a card. |\n| `app.add-filed` | The Lists adding rows of one table file them by one link, the first such List\'s: a List over that table filed by another link lists its rows and adds none. |\n| `app.default-breaks` | A node the platform draws where the tree states none leaves the App refusing nothing more: where it would, the tree states that node itself. |\n| `app.adds-field` | A picker\'s `add` names fields of its table a person writes, each once. |\n| `app.picks-only-field` | `add: false` stands at a link, once, to an entity whose add the app states. |\n\n<!-- generated:end rules-app -->\n\n## Reading rules\n\n<!-- generated:start rules-reading -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `reading.value` | A Reading sums a number, never a percent \u2014 percents do not add up; a value of `{ avg }` reads one. |\n| `reading.average` | `{ avg }` averages a Reading\'s `value` over the rows holding one, read against a plain `target` \u2014 never a value `limits` bounds, whose bound is a sum. |\n| `reading.split` | `by` and `across` split by one value each row holds \u2014 a single select, a one-row link, one member or a yes/no, stored or a formula, a lookup through one-row links as the field it reads \u2014 or by the bands `tiers` cut a number read in one unit on every row; a date `by` splits them per period, as a trend; a grid crosses two different fields. |\n| `reading.over` | `over` is a date: the row\'s own, stored or a formula, or its parent\'s through a lookup over a one-row link. |\n| `reading.where` | A reading\'s and a Query\'s `where` keep rows by a select\'s options, a yes/no\'s `true` or `false`, a number or date\'s bounds (`gt`, `gte`, `lt`, `lte`; a date\'s `{ "now": true }`), `{ "empty": true \\| false }`, or a member field holding the reader (`{ "me": true }`); a List\'s `where` also keeps rows by `<link>.<field>`, a select or yes/no of the row a one-row link names; a Query\'s `where` also holds a field to what the call states (`param`, `from`, `to`). |\n| `reading.target` | `target` reads a value nothing bounds \u2014 a value `limits` bounds reads against the sum of its bound. |\n| `reading.better` | `better` needs `over`: a change is read over the periods its date places the rows in. |\n| `reading.list-app` | A List\'s `app` is the id of an app whose first List is over the List\'s own entity. |\n| `reading.progress` | A Progress reads dates of its rows for `planned` and `finished`, a number for `done` and `weight`, and rows `measured` through a one-row link to them, by a date of theirs and a number of theirs. |\n| `reading.alias` | A Reading\'s or Progress\'s `key` is its own among the App\'s Readings and Queries, a log\'s read by its entity before it (`<entity>_<key>`); a Progress above a List reads the List\'s own rows and states no name of its own: its `key` is the one its place gives it, `register_reading_<position>`. |\n| `reading.tabs` | A dashboard with Tabs holds every Reading and List inside a Tab, and every Tab holds one at least; without Tabs, none sits in one. |\n| `reading.restate` | A dashboard\'s count tile never counts the rows a List beside it lists under the same `where` and tab: the List\'s head counts them. |\n| `reading.period` | A dashboard\'s `period` is the one it opens on, and it stands only over a Reading placed in time by `over`. |\n| `reading.funnel-place` | A Reading with `steps` (a funnel) stands in a part of a Record: a band and a dashboard draw none. |\n| `reading.on-hand-split` | A Reading with `on_hand` compares what is owed with what is on hand per value of one field, or two: its `by` and `across` name fields, never bands. |\n| `reading.average-single` | An average (`value: { avg }`) is a single figure\'s: its Reading states no `on_hand`, `by` or `across`. |\n| `reading.grid-rows` | `across` splits a grid\'s columns, and stands only beside the `by` splitting its rows. |\n| `reading.tab-holds` | A register\'s Tab narrows its rows and holds no nodes: Readings stand beside it in the List. |\n| `reading.band-off` | `readings: false` turns off the band a register leads with by default: a List stating it states no Readings or Progress, which are the band. |\n\n<!-- generated:end rules-reading -->\n\n## Statement rules\n\n<!-- generated:start rules-statement -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `statement.lines` | A Statement\'s `lines` are numbers of the record, each once \u2014 the first stating no `op`, every other one, the last an `equals` \u2014 each `equals` a formula, rollup or lookup, and a `rate` a number of the record. |\n| `statement.rows` | A line\'s `parts` read rows of an entity holding `same`, a one-link of the record\'s to the same entity; `at`, a moment of those rows, and `before`, a moment of the record (datetimes, never days), stated together; their `columns` and `figure` are those rows\' fields, the figure a number. |\n| `statement.parts-keyed` | A line\'s `parts` add up to its figure, so no person keys it: a formula, rollup or lookup, a value the server copies when the record is made (`default_from`) that no `create` asks and no save or Act writes, or a stored number the App\'s saves leave out \u2014 the line draws it read; each part\'s `figure` is frozen when its row was certified \u2014 a stored number an Act fills, the server copies or the App\'s saves leave out, never one a person keys or the server recomputes \u2014 and no person keys the moments `at` and `before` either. |\n\n<!-- generated:end rules-statement -->\n\n## Check rules\n\n<!-- generated:start rules-check -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `check.field` | A Check\'s `field` is a yes/no or text formula of the rows of the List it stands in. |\n| `check.of` | A Check stands in the register List or in a child List the Record lists. |\n| `check.not-placed` | A Check\'s field is never also placed on the record as a Field \u2014 the line under the head says it. |\n| `check.blocks` | A Check\'s `on` names what it refuses: `"update"` (the saves), an Act\'s `key`, or `"all"` (every Act and save); a child List\'s Check refuses that row\'s saves and the Acts of it, never the record\'s. It binds this App alone. `"delete"` refuses this App\'s removal of the rows and is kept for a rule no check of the table states: a Delete is the table\'s checks\' to refuse. |\n| `check.resolve` | A Check\'s `resolve` names an Act of this App on the rows the Check stands on, pressed on one row, not refused by the Check, and drawn as a button \u2014 never a correction. |\n| `check.restates-meter` | A warning reading a gated meter\'s figure against its bound or pass mark is refused \u2014 the meter draws it. |\n\n<!-- generated:end rules-check -->\n\n## Register rules\n\nThe rules of the register List\'s props: columns, filters, search, group, sort, layout, tabs, period and export.\n\n<!-- generated:start rules-register -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `register.export-report` | A List\'s Exports each have their own `label`; `per` picks one option, person or one-link row \u2014 never of the status, the folder or a field its `where` fixes. |\n| `register.export-template` | A template an Export fills reads at its root only `title`, `lines`, `readings`, `at`, `dates` and its rows under the List\'s entity alias \u2014 an entity named none of those \u2014 and is the document of no Act. |\n| `register.column-drawn` | A List\'s `columns` never name what the row draws itself \u2014 its title, subtitle, image, status, figure, or the bound its figure\'s meter reads against \u2014 and name a field once. |\n| `register.columns-budget` | A List\'s `columns`, each at the width of its kind, fit the room a desk leaves beside its name and the row\'s own status, deadline and figure. |\n| `register.filter` | A List\'s `filters` name a select, a member, a link, a date, a yes/no or a number of the List\'s own rows \u2014 a lookup through one-row links as the field it reads \u2014 never the status (its chips), nor one `where` fixes. |\n| `register.filter-split` | *Noted, never refused:* A `filters` entry on a field a Reading of the List with `by` or `across` splits the rows by \u2014 a press on its part narrows by it too; a member\'s filter, which narrows to the reader\'s own rows, is never noted. |\n| `register.filter-unshown` | A `filters` entry, a narrowing the List `opens` on, and a press in its band (a Reading\'s part, a grid\'s cell, a figure\'s `where`) name a field its rows show \u2014 a `columns` entry; the row\'s title, the line under it, its picture, status or figure, a meter\'s bound, or the unit or currency a drawn figure is read in; a deadline; a warning its line says; the group or lane it stands under \u2014 or a formula, drawn where every field of its own row it reads is (through other formulas too; one reading no field is drawn nowhere). A narrowing to the reader (`{ "me": true }`) needs none. A tree leaves out a `filters` entry no row shows and says so (name the field as a column or in the row\'s line to offer it); the other two are refused. |\n| `register.tiers` | `tiers` band a number read in one unit on every row; a figure read in each row\'s own unit or currency is named bare. |\n| `register.search` | A List\'s `search` names text, a code, or a link \u2014 matched by its row\'s title. |\n| `register.sort` | Each `sort` key of a List or a Tab names a field of the List\'s rows that holds an order \u2014 never files \u2014 and no field twice. |\n| `register.tabs` | A List\'s Tabs each have a `key` of their own, none the entity\'s alias or one a report template holds (`title`, `lines`, `readings`, `at`, `dates`, `period`, `per`); a Tab states one of `dated` and `open` (dates of its rows, `open`\'s two different) or `totals` (a child\'s rows windowed by the period); its `where` and `sort` hold as the List\'s do, and never narrow by the status, which its chips split. Only a table or cards List has Tabs, and a Reading\'s `tab` names one of them. |\n| `register.tab-totals` | A Tab\'s `totals` name a child linked to the List\'s entity by a one-row link (a child linking the List\'s entity twice cannot be totalled), a date of the child, never a moment or a span, and 1 to 6 columns each under its own alias and label; a column\'s `value` is a number of the child, its `where` holds over the child\'s selects and yes/nos, and a value read in each row\'s own unit or currency stands only where that select of the child looks up, through its link, a single select of the List\'s entity, and the child links to no Book\'s rows. A Book\'s rows are totalled by the child\'s one link to them, by one field to one row; a Book the child links to not at all totals nothing on its rows (`app.book-lacks`). Only a table List has them, and no Tab states `totals` beside `dated` or `open`. |\n| `register.group` | A List\'s `group` names one value per row \u2014 a single select, a one-row link, one member, a date, a yes/no or a text, a lookup through one-row links as the field it reads \u2014 the status too, but never on a task list\'s block, whose chips narrow it; only a table or cards List groups, and a gantt into its lanes; grouping by the title needs a `row.subtitle` to lead each row. |\n| `register.readings-own` | A Reading in a List reads the List\'s own entity; another entity\'s stands on a dashboard. |\n| `register.readings-bands` | A Reading in a List that bands a number by `tiers` has that field with the same tiers among the List\'s `filters`: a press on a band narrows the rows by that filter. |\n| `register.readings-restate` | A Reading with no `by` in a List never sums the row\'s figure, counts the rows, or counts one stage\'s rows, unless it is placed in time (`over`): the summary line totals the figure and the status chips count the rows, but neither draws a change against the last period. |\n| `register.where-opens` | A List\'s `where` holds its own selects, yes/nos, bounds and blanks (a child List\'s or the register\'s), and `opens` its selects and yes/nos and a member field holding the reader (`{ "me": true }`) \u2014 or, on a status whose `stages` have owners, the rows waiting on the reader; a field `where` fixes to options is never a filter nor opened otherwise, and `opens` stays among the options `where` keeps. A List laid out as a `plan` keeps its items by options and yes/nos alone. |\n| `register.roster` | A List with `layout: "roster"` holds the List of its cells: the cells\' entity has one single link to this one and a date on its line (a field of the row itself, or a lookup of its parent\'s date), a roster draws no `columns`, and an `expect` (with `of`) stands only on the cells List. |\n| `register.roster-expect` | A roster cells List\'s `expect` is a single select of its rows, and its `of` this entity\'s multi-select whose every option is one of `expect`\'s. |\n| `register.gantt` | `layout: { gantt }`: `start` and `end` (absent, the entity\'s first `due`) are two different dates of the row, both days or both moments; each diamond (`milestones`) another date of it, each planned date read as `start` is, `progress` a percent and `after` a link to rows of the same entity. |\n| `register.lanes` | `layout: { lanes }`: `by` is a one-row link to an entity with a `records` entry, the rows name a date on their line (a field of the row itself, or a lookup of its parent\'s date), a lane block\'s two ends are read alike (two days or two moments), and `loads` stands with it. |\n| `register.plan` | `layout: { plan }`: `at` is a one-row link to an entity whose `records` states a `plan_place`, `across` and `up` two different numbers of the row, `fill` a single select; a plan List has no Tabs, `group` or `attention`. A write that sets `at`, `across` or `up` is refused unless it clears all three, or places the row at a free cell of a bay (a row from 1 to the bay\'s `across`, a tier from 1 to its `up`, resting on a present item), or at an area with neither. |\n| `register.plan-unique` | *Noted, never refused:* A plan\'s items (the List\'s table and each Book) state `unique` on their place link, `across` and `up`: the app\'s moves refuse a taken cell, and the unique makes every other write hold it \u2014 the note names a table that states none. |\n| `register.plan-mark` | A plan\'s `mark` names a single select of the row and an option of it by alias: a row holding that option says its label on its box and is hatched in the option\'s colour, which the legend names by its label. A Book that holds the select must hold it as a single select with that option; a Book lacking it never marks its rows (`app.book-lacks`). |\n| `register.plan-size` | A plan\'s `size` names a single select of the row and, by option alias, the box (`across`, `along`, `up` \u2014 positive, in the one length unit the places\' `size` states) of each option: a row holding one is drawn as that box, any other at its place\'s cell. A Book that holds the select must hold it as a single select; a Book lacking it draws its rows at their places\' cells (`app.book-lacks`). |\n| `register.plan-box-size` | *Noted, never refused:* A plan whose places state a `size` and whose rows\' select options are ISO 6346 container size-type codes (`20DC`, `40HC`) states the box of each in `size`, from the ISO 668 dimensions, else every box is drawn at its place\'s cell. |\n| `register.loads` | A lanes layout\'s `loads` names one to 2 numbers a lane block adds \u2014 never a percent \u2014 each against a number of the lane\'s row, in one unit or two of one dimension. |\n| `register.band-pivot` | *Noted, never refused:* A Reading with `across` (a grid) among a List\'s Readings: its grid reads on a dashboard, and the band leads with a figure (a Reading with no `by`). |\n| `register.headline` | *Noted by the designer on a write, never refused:* A List over rows that fall due or carry money and have no status whose steps the chips count, laid out as a table, cards or a gantt, has Readings \u2014 they split the rows by a field they show. |\n| `register.span` | *Noted, never refused:* Rows that each span a run of days or hours \u2014 two dates on their line, a `frees`, a `no_overlap` booking them on what its `by` links to, or one required date and the table\'s `due` where the line names no date \u2014 are drawn in time by some List over them (a `layout` of calendar, lanes, gantt or roster) \u2014 the note names the app to add over them, laid out as lanes on what each one books, else as a gantt (a run held by a date and the `due` also asks for both dates on the line). |\n| `register.where-bound` | A bound read from the record (`{ "record": \u2026 }`) in `where` narrows only a List that reads its rows \u2014 `same`, `app`, or a Record with `edit: false`. |\n| `register.where-reader` | A List\'s `where` keeps its rows for every reader and never names the reader (`{ "me": true }`): `opens` opens a List on the reader. |\n| `register.props` | A node states only the props of its place: a List those of the register, a log, a roster\'s cells, a rows, agenda, timeline or read-only List of a record, a document or a dashboard; the Record of a List below the register `edit` and its children; a dashboard\'s Tab its `key`, `label` and children. |\n| `register.holds` | A List holds what its place draws: the register its Tabs, Readings, Progress, Exports, Acts, Checks, Books, Record and, under a roster, the one List of its cells; a child List its Acts, Checks, Record and, with `under`, the List of its documents; a document and a roster\'s cells their Acts, Checks and Record. |\n| `register.layout-place` | The register lays out as a table, cards, a calendar, a roster, a gantt, lanes or a plan; a child List writing its rows as a table, a checklist or a desk; any child List as an agenda or a timeline. |\n| `register.row-restates` | A child List and a log read their rows as `records` states them: their `row` states a picker\'s add or a card on the title or a subtitle, never another `subtitle`, a `figure`, `due`, `image` or `status: false`. |\n| `register.edit-column` | An edit column (`{ "field": \u2026, "edit": true }`) stands in a child List, which writes its rows in place: the register\'s rows are written where they open. |\n| `register.remove-roster` | A roster cells List\'s `remove: true` offers the Delete of the roster\'s rows, so it stands only beside a roster over rows the App writes. |\n| `register.attention` | `attention: false` keeps a table\'s or cards\' rows in the List\'s own order \u2014 a first-come queue; a calendar, lanes, a gantt and a roster stand their rows in time and lift none. |\n| `register.unworked-stage` | *Noted, never refused:* A register lists rows (every open one, else those its `where` keeps) at a stage that no Act of the App is offered at and no Section stands at, though the App states the stages it works: the rows only sit in its list, so keep them out with `where` or leave them to the App that works them. |\n\n<!-- generated:end rules-register -->\n\n## Block rules\n\nThe rules of what a part holds: a child List, a Field gallery or prose block, a Reading, a desk, a timeline or an agenda.\n\n<!-- generated:start rules-block -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `block.child-link` | A child List reads a child linked to the record; `via` names the link where the child links more than once. |\n| `block.row-blocks` | The Record of a List below the register \u2014 a rows List\'s, a document\'s under its lines, a roster\'s cells\' \u2014 holds its Fields and the Lists of its rows\' own children, each a child List as one level up over an entity linking to that List\'s, checked against that List\'s entity; nothing else. A List below a row states no `under`, `pick`, `expect` or desk, and a List stating `expect`, `under` or a desk holds no List below its rows. |\n| `block.column-drawn` | A child List\'s `columns` never name what the child\'s row draws (title, subtitle, image, status, figure, or the bound its figure\'s meter reads against) nor its link back, and name a field once; an `expect` List\'s figure is its filled-in-place column. |\n| `block.columns-budget` | A child List\'s `columns`, each at the width of its kind, fit the room a desk leaves beside its name and the row\'s own status, deadline and figure. |\n| `block.title-back` | Where a child\'s title is its link back to the record a child List lists it under, its first other `row.subtitle` names it there \u2014 a text, a code, a select, a link or a member, never a number or a date. |\n| `block.where` | A child List\'s `where` names single selects the child requires, by options they hold \u2014 one held at a single option is never also a column \u2014 and the child\'s yes/nos by value, numbers and dates by bounds, and fields by `{ "empty": true \\| false }`. A yes/no, a bound or a blank is never held: a child List adds rows only at a stored yes/no\'s start, adds none under a bound or a blank, and never picks, expects or stands lines under documents. |\n| `block.under` | `under` names the child\'s one-row link to a document entity that pairs a many-link back and is read under this record by its one link; the lines\' columns never read the document (its heading does) nor hold long text. |\n| `block.under-once` | Documents drawn over their lines (`under`) are never listed again as a List of their own. |\n| `block.edits` | A child List\'s editable columns (`{ "field", "edit": true }`) name at most two of the child\'s columns or its figure that the App\'s saves of the child write \u2014 a number, a date, a few words, one option or a one-row link, never a files or long-text field, the List\'s `via` nor a link it must name alike (a link\'s `same`) \u2014 never on an `expect` List. |\n| `block.remove-documents` | The documents List inside a child List with `under` states `remove: true` only over documents the App writes. |\n| `block.compare` | A child List\'s `compare` names two different fields of the child, `before` and `after`: two files fields, or two values of one type (text but long text, a number, a date, a single select). Never with `expect`. Over files on a checklist layout, `before` is the check\'s `photo`. Over values: neither side is a column or something the row draws already (title, line, figure, status, deadline), two numbers share a notation, no column is long text, never on a checklist layout. |\n| `block.current` | A child List\'s `current` names a date or number of the child (stored, or a formula yielding one) that orders a set of revisions: the row holding the latest value is current, the rest fold as superseded. Never with `group`, `under`, `expect`, `checklist`, `compare`, `post` or `pick`, nor beside a column of long text. |\n| `block.checklist` | A child List with `layout: { checklist }` reads a task entity\'s rows as checks \u2014 never under documents, grouped, edited in place or picked together by an Act over several, its columns no files; `photo` names a files field of the child, `clause` a one-row link of it, `failed` options of its status, none `closed`; `passed` a yes/no of it, stored or a formula, setting each mark (yes passed, no failed, empty still to check) and standing without `task` or a status. With `expect` the lines of a standard are the checks, each marked and filled by the block\'s columns (a measured value, a note, a photo), so `photo` and `clause` are not named. |\n| `block.pick` | A child List\'s `pick` links rows of the child through `via` \u2014 an optional one-row link a person writes, no Act writes, no other rule compares rows by and no `under` List stands lines under \u2014 paired with a many-link of the record its own save writes; the child\'s `read_scope` reads a row naming no record through `via`; never with `expect` or `under`, nor where the App\'s saves leave out the child or the many-link. |\n| `block.narrow` | A child List\'s `filters`, `search`, `group` and `sort` follow the register List\'s rules over the child\'s rows, its status among them but on a task list (its chips); never on the link back to the record or a select its `where` holds at one option, nor on an `expect` List; an `under` List is grouped by its documents. |\n| `block.expect` | `expect` names the child\'s title \u2014 a single select or a one-row link a person writes, never the link back \u2014 on a child added in the List; its `columns` name a field filled in place \u2014 unless the List\'s layout is a desk, whose lines are ticked \u2014 and its `create` takes the file where the child is pictured by one. |\n| `block.expect-fills` | An expected line is made from its key and its first filled column \u2014 a desk\'s from its key alone, by its tick \u2014 so every other field the child requires has a default, a start or a `default_from`, or is read first. |\n| `block.expect-of` | An `expect` object\'s `of` or `only` needs a select: `of` names the record\'s multi-select of the title\'s options; `only` names options of a single select, never with `of` or a `where` on it, one List per option; an Act\'s `paper` is drawn in one. |\n| `block.post` | A child List with `layout: { desk }` needs `expect` on a one-row link \u2014 never a select, nor with `under`, `pick`, a checklist layout, `compare` or a `create` list, its line made by a tick from its key alone: its `bodies` are plain text fields of the record a person writes, drawn nowhere else on it; its `text` is a text of the child, stored or a formula yielding text \u2014 a formula reading only stored values of its row, lookups through the link back to the record or through `expect`, and formulas of the row over those, never the clock; its `media` a files field of the record and its `link` a text field of the record; its `network` a single select of the entity the link names, and `networks` holds options of that select, by alias, each to one of facebook, instagram, threads, x, linkedin; its `group` is another single select of that entity. |\n| `block.funnel` | A Reading\'s `steps` (a funnel) are 2 to 8 numbers of its child, each named once \u2014 stored, a formula or a rollup \u2014 all in one notation, never a percent nor a unit read on each row. |\n| `block.list` | A read-only child List (`same`, `app`, or a Record that is `edit: false`) lists the rows of an entity that link to the record by its one link to it (`via` where it has several); with `same`, a one-row link of the record, it lists the rows sharing that link\'s row with the record \u2014 the record left out \u2014 by the listed entity\'s one link to that row\'s entity (`via` where it has several). `same` stands in a Record, never on a dashboard. Its Record\'s Fields \u2014 at least one, each a field of the listed entity named once, never its title nor one of the `columns` \u2014 are what a row shows, read-only, when it is opened in place. |\n| `block.list-opens` | A read-only child List\'s row opens in the record\'s drawer as a record: its entity is one a List of the App draws \u2014 the register\'s, a Book\'s, or a log\'s \u2014 or the List\'s `app` names the register app that works it. |\n| `block.figures` | A Record\'s own figures (Readings with no `entity` and no `by`, whose `value` names the field) are 2 to 8 numbers of the record no person keys \u2014 a formula, a rollup, a lookup through links to one row, or a stored number the App\'s saves leave out \u2014 each placed once on the record. |\n| `block.agenda` | An agenda List\'s child names a date on its row line (`records` title or subtitle); the layout\'s `start` is a date of the record. |\n| `block.timeline` | A timeline List\'s child holds a date to place its entries by. |\n| `block.create-boolean` | A document\'s and a roster\'s cells\' `create` is `true` or `false`: their add asks the table\'s defaults. |\n| `block.via-path` | A `<link>.<link>` `via` reaches rows through a row of their own and only reads them: a List writing its rows lists a child linked to the row it stands under. |\n| `block.read-label` | A List that only reads its rows \u2014 a read-only child List, an Outside\'s List, a dashboard\'s List \u2014 states its `label`. |\n| `block.create-empty` | A child List that adds rows asks the fields of its add: where the table\'s defaults ask none, state `create: false`, or the fields it asks. |\n| `block.read-scope` | A child read under a record whose rows a `read_scope` narrows states its own \u2014 a row rule is not inherited, save the status history\'s, which takes its record\'s. |\n| `block.agenda-note` | *Noted, never refused:* Rows each on a day and within it (at an hour, in a part of the day) listed as a table: a List with `layout: { agenda }` draws them by day. |\n| `block.under-note` | *Noted, never refused:* Rows holding a paper that cover another List\'s lines, listed apart with no add of their own stated: `under` draws them over the lines they cover. |\n| `block.timeline-note` | *Noted, never refused:* Rows each what happened on a day and who \u2014 a date on their line, a member, no status and no figure \u2014 listed as a table: a List with `layout: "timeline"` draws them as a log, newest first. |\n| `block.expect-note` | *Noted, never refused:* Rows each a kind of paper, listed with no `expect` and no `where` on the kind, that no Act files a call into or `fills`: the lines still missing read as what a record needs. |\n\n<!-- generated:end rules-block -->\n\n## Record rules\n\n<!-- generated:start rules-record -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `record.placed-once` | A Field, a child List, a Reading, a Statement and an Act each stand once in a Record: its head draws the title, subtitle, image, status, figure and folder, and a field\'s one other place is a part\'s Fields, a gallery or prose Field (`block`) or a Statement \u2014 save the image, which one files gallery may draw again, whole, the figure, which a Statement draws as what it comes to, and rows one List prices and another compares (`compare`); two places whose `when`s never hold together (a select at options the other\'s lacks) each draw it, and a plain child List (no `expect`, `under`, `pick`, `compare`, nor a `layout` of desk or checklist) likewise. |\n| `record.foot-total` | A child List\'s foot totals the child\'s figure and each summed column over the rows it lists; the record\'s rollup summing that field over those rows (the same `where`) is that foot, never also a Field of a part. |\n| `record.fact-restates` | A Field of a part never looks up what its link already reads where it stands: the linked row\'s title beside the link, the figure or bound its meter reads on the head\'s line. |\n| `record.section-holds` | A part (Section, Side, Page) holds Fields, child Lists, Readings, Progress, Statements or Acts. |\n| `record.compose` | A Record\'s `compose` is stated instead of parts (it holds no children), on a page `door`, by an App with no Books whose records have no owned stages (`status.stages`) and no picture (`image`) \u2014 a composed page draws neither. Each name is a field of the entity, an Act of the app on the record or a child entity linked to it; one `Title`, and each field, Act and child drawn once \u2014 a field by `Field` or `Document`, never the title or the status, which `Title` and `Status` draw. The first `Act` the page itself draws is its next step and every other one secondary; an `Act` is a button alone, never one making a set of papers. A `Bar` holds no `Rows` and no `Document`, and an `Act` in it that destroys or corrects what is done (`danger`, or a move back) stands in the record\'s \u22EF instead (the Bar piece\'s own row says how it draws). A `Panel` draws nothing in the page: it is one entry of the record\'s \u22EF, named by its `title`, opening its pieces in a panel beside the page; it stands directly in the root `Column`, holds `Rows`, `Field`, `Act`, `Group` and `Document` \u2014 never `Title`, `Status`, `Bar`, `Copy` or another `Panel` \u2014 and an `Act` directly in it ends the panel\'s name, secondary. A `Group` is headed once, by its `title`: holding one `Rows`, that heading carries the rows\' count and their add in place of the rows\' own, and the `Act`s the group holds end its line. A `Copy` takes a text field\'s words as the page holds them now, a `Document`\'s words not yet saved among them; a field a `Copy` alone names is read, never written. |\n| `record.sections` | *Noted by the designer on a write, never refused:* A List laid out as a table, cards or a gantt opens a Record whose Sections hold the fields people write, a files Field per paper, a child List per child and the record\'s Acts. |\n| `record.header-budget` | *Noted by the designer on a write, never refused:* A Record\'s head says what the row is and where it stands \u2014 its title, line, picture and status \u2014 and at most two more facts, one of them a deadline at most; the rest stands in a Section. |\n| `record.children` | *Noted by the designer on a write, never refused:* A table some App records, whose rows other rows each belong to (a one-row link to it, never its status history), draws some of them under its row \u2014 a child List in its Record, a List with `layout` roster or lanes by that link, or an App over them working inside one of its rows at a time (`scope`). |\n| `record.at` | A Section\'s `at` names options of the record\'s status; a record with no status has no steps. |\n| `record.at-once` | A stage is the work of one Section: two Sections naming one option of the status in `at` hold some Field to values apart in their `when` \u2014 a `when` holding the status keeps its Section off the options it leaves out. |\n| `record.section-page` | A Page stands only on a page `door` \u2014 never a drawer\'s or one beside the List \u2014 is never a step (a Page has no `at`), has a `title` naming an address no other Page shares (a letter or a digit at least), and is never every part. |\n| `record.side` | At most one Side stands in a Record, on a page `door`, and it is never also a Page. |\n| `record.beside` | A Record with `door: "beside"` opens beside a table List \u2014 never a board, cards or a calendar, which draw no list to stand beside. |\n| `record.history` | No part of a Record reads the entity\'s status history (`status.history`): the rail reads each move. |\n| `record.section-acts` | An Act in a part works on the record itself; an Act in a child List stands on the child\'s row, and an Act in the register List with an `on` on the register\'s rows. |\n| `record.act-in-section` | Every Act of the record stands in the part whose work it is; only a correction no part owns \u2014 `danger`, and no other status move offered at the same moment \u2014 or an Act an agent runs for the person (a `workflow` or an `intake` moving no status), placed in the List and in no part, waits in the head\'s \u22EF. |\n| `record.move-home` | A status move offered at one step stands at that step\'s foot when it moves the work forward, to an exit, or back to a step it passes anyway; one offered at several steps, or moving back into a detour, stands in the Section whose `at` holds the status it enters. A tree\'s status move written in a Section with no `at`, or in none, is placed where this puts it and said. |\n| `record.act-staged` | An Act at a staged Section\'s foot is offered only during that Section\'s steps: its `when` holds the status to options the Section\'s `at` names. A tree\'s Act with no `when` at the foot of a Section staged at one step is offered at that step and said; a correction stays offered at every step, and an exit is refused. |\n| `record.act-hidden` | An Act is never offered only while its part\'s `when` hides the part. |\n| `record.outside-place` | An Outside stands directly in a Record whose table states a stored status select, and its `key` is unique in the App: a link to a record names it. |\n| `record.outside-at` | An Outside\'s `at` names options of the record\'s status that no stage owns, and no two Outsides of a record open during the same option. |\n| `record.outside-field` | An Outside\'s Fields are read as values, each a field of the record shown once, never `edit`, `when` or a label of its own. |\n| `record.outside-list` | An Outside\'s Lists read the child rows under the record as they are: their `columns`, `label` and `where` only, never an add, a Delete, a pick, a filter, a search, an `app`, a link that opens, or a bound read from the record. |\n| `record.outside-act` | An Outside\'s Acts are its alone and each ends the visit: `when` holds the status to `at`, `set` moves it to an option outside `at`, `asks` are text, numbers, dates or listed options, `requires` names fields the Outside shows or the act asks, a `sign` is by a text field with no witness, and none states `on`, `of`, `workflow`, `templates`, `paper`, `intake`, `record`, `review`, `open`, `danger`, `recommends` or `me`. |\n| `record.field-edit` | An Act that only writes one field of its record, not the status, from the one input it asks is that field\'s edit: the Field rests in the part as a control. |\n| `record.holds` | A Record holds parts (Section, Side, Page) or the Fields of its one part, never both; the Record of a Book, or of a child List no row opens in place (an agenda, a timeline, a read-only List), holds Fields alone. |\n| `record.field-heading` | A Field drawn as a control states no `label` or `when`: only a gallery or prose Field (`block`) has a heading and a condition. |\n| `record.section-owners` | *Noted, never refused:* A Section holding acts offered at stages that different owners work \u2014 a role apiece, or a member field apiece \u2014 is two jobs in one: each owner\'s part is a Section of its own. |\n\n<!-- generated:end rules-record -->\n\n## Act rules\n\n<!-- generated:start rules-act -->\n\n#### Rules the check enforces\n\n| Rule | |\n|---|---|\n| `act.paper-note` | *Noted, never refused:* A made paper kept in a field of a record that lists its papers by kind: it is one of those lines. |\n| `act.moves` | *Noted by the designer on a write, never refused:* Rows a stored status stages towards `closed` options are moved by an Act of some App over them, or by its authored `workflow` \u2014 each open option moves to the next, the last to the first closed one. |\n| `act.set-not-asked` | A field an Act `set`s (an option, a value, `{ "now": true }`, `{ "me": true }`, an input) or `sign`s is the Act\'s outcome: the App\'s `create` never asks it, and a required one takes a `default`. A `formula` only starts a value, which a person may change after. |\n| `act.asks-set` | A field an Act `asks`, `set`s, `sign`s or `writes` is stated by one of them: what the Act writes is its outcome, never also asked, and no field is written by two clauses of one Act. |\n| `act.writes` | `writes` stands beside `workflow` and names stored fields of the acted row, each once, that no `set` (its own or another Act\'s) states. |\n| `act.starts` | `starts` (an ask `{ field, starts }`) starts a field the Act asks \u2014 `today` a date or a moment (never a span), `me` a member \u2014 for the reader pressing it, who changes it at will. |\n| `act.set-formula` | `set: { <field>: { formula } }` reads fields of the acted row and computes what the field holds \u2014 a number, a date, text, a yes/no or a single select\'s option (`{field:option}`), typed as the entity\'s own formulas are \u2014 never the status; a bound a write rule holds the field to is checked on the computed value as the Act runs. |\n| `act.of` | An Act in a child List works on that child: the one row pressed \u2014 or, with `on: "ticked"`, the rows ticked together where the record lists them as tasks \u2014 never the rows in view. |\n| `act.files-written` | An Act\'s `writes` names no files field: a file an Act makes is a paper, filed in a slot it names with `paper` (a child List with `expect`). |\n| `act.writes-line-key` | An Act never writes the link or the key naming an expected line under its record \u2014 a line\'s key is stated by filling it. |\n| `act.makes-or-runs` | An Act makes a document (`template`, `templates`) or runs an authored `workflow`, never both \u2014 the workflow makes what it needs. |\n| `act.does-nothing` | An Act does something: it `set`s a value, `asks` a field, runs a `workflow`, makes a document, or takes an `intake`, a `record` or a `sign`, or `open`s an app. |\n| `act.view` | An Act over every row in view (`on: "view"`) writes (`set`), makes a document or runs a workflow, and states no `requires`. |\n| `act.requires-drawn` | Every field an Act `requires` is drawn where the Act stands \u2014 its part\'s Fields, a child List\'s, its `asks`, or an unstaged part\'s where its own is unstaged \u2014 never the head alone. |\n| `act.requires-written` | A field an Act requires that only an Act writes is written by an Act offered together with it, or asked by the Act itself. |\n| `act.recommends` | A field an Act `recommends` is not also in its `requires`. |\n| `act.set` | `set` writes each field a value it holds \u2014 an option alias, a number, a yes/no, a text, `{ "now": true }` on a date, `{ "me": true }` on a member, `{ "input": "<name>" }` of an input the Act asks of the same kind \u2014 or clears an optional one with `null`. |\n| `act.into` | `into` is a files field of the one record the Act is pressed on, beside its `template` or `templates`. |\n| `act.paper-list` | A List a made paper names is one it can list: none keeps its rows by the reader, and no name is reached by a child List that writes its rows and one that only reads them, or by two of either, at once. |\n| `act.paper` | `paper` is a kind the record lists its papers by, on one record \u2014 never beside `into` or `templates`; one maker per kind. |\n| `act.templates` | `templates` names each template once, kept in `into`, on one record pressed alone \u2014 never beside `template` or `asks`, in a child List, or with an `on` over several rows. |\n| `act.intake` | `intake` names a child the record lists in a child List with `expect` on its title \u2014 a single select, the paper\'s kind \u2014 and is narrowed by no `where`, holding exactly one files field and needing nothing an added line does not fill; the Act runs on one record, states `fills`, and states nothing but its `label`, `when` and `requires` beside them. |\n| `act.record` | `record` names a child the record draws in a timeline List or a child List with no `expect`, `under` or `where`, linking back by one single link, its row needing nothing the recording does not write: `audio` and `video` files fields, `transcript` text, `at` a date, `by` a member, each its own field; on one record, stating only `label`, `when`, `requires` and `fills` beside it. |\n| `act.sign` | `sign` runs on one record, never a child\'s row: `into` a files field, `by` a text field the signer types or a member field set to the person pressing, `at` a date, `witness` a member field \u2014 stated exactly beside a text `by` \u2014 each a field of its own that the Act does not also ask or set, and no other Act signs into `into`. |\n| `act.open` | `open` opens another app at the record and does nothing else: an Act on one record of the app\'s own, stating only `label` and `when` beside it. |\n| `act.open-app` | An Act\'s `open` is the id of an app of this workspace that reaches the record\'s table: a register over it, or a custom-code app declaring no table or declaring it. |\n| `act.fills` | `fills` names each once, only beside `intake` or `record`. Beside `record`: a field of the recording\'s row the recording does not write, never a link. Beside `intake`: a field of the record, `<link>.<field>` on the row a required one-row link names (never beside that link), or a child the record adds rows of in a child List with no `expect`, `under` or `where`, filled in the fields its `create` asks \u2014 every one a new row needs among them. Each field filled is one a person writes \u2014 text, a number, a date, a yes/no, options or a single link\'s row \u2014 never a status, a field an Act writes or a link `same` narrows; a link\'s row is one the App reads of its entity. |\n| `act.fills-drawn` | *Noted, never refused:* A field `fills` writes that the record draws nowhere: the review shows the change, the record does not. |\n| `act.fills-new` | *Noted, never refused:* A child `fills` adds rows of, whose natural key the papers cannot state: every row read is added, so the same papers read twice list them twice. |\n| `act.input` | An input has a name of its own \u2014 never another ask\'s, a field\'s, or the record input its workflow takes (nor `approved` beside `review`) \u2014 `options` exactly when it is a select, `entity` exactly when it is a link, and a `default` field holding what it states. A `file` input is read by the Act\'s authored `workflow` alone, starts empty, and is never written into a field by `set`. |\n| `act.review` | `review: true` stands beside an authored `workflow` on an Act over the record, the ticked rows or every row in view, in an App over one entity \u2014 never in an App with Books, nor beside `set`, `template`, `templates`, `intake`, `record`, `sign` or `confirm`, nor in a child List, and it asks inputs only, none named `approved`. Its workflow is that Act\'s alone, run by no other Act. The workflow calls only steps that read \u2014 no agent \u2014 and returns a changeset of the entity\'s table in `data.review`, the same for the same inputs and rows; the platform writes the rows `approved` names, each only while the run proposes it as the caller read it. Over every row in view (`on: "view"`) it is handed no rows and states no `when`: its workflow reads the rows it may change, whatever the view is narrowed to, and no bound on a run\'s rows applies. |\n| `act.set-tagged` | An Act\'s `set` states the moment as `{ "now": true }`, the person pressing as `{ "me": true }` and an input as `{ "input": \u2026 }` \u2014 never the strings `"now"`, `"me"` or `"input:\u2026"`. |\n\n<!-- generated:end rules-act -->\n\n## What the workspace remembers\n\nThe workspace holds every table, field and select option under an id that never changes, whatever its\nlabel or alias becomes. `set_tables` answers each table\'s id, and an\napp\'s body names tables and templates by those ids, so a relabel on either side is a rename and no body\npoints at another table by accident.\n\nA table `set_tables` refuses to write, or an id the workspace no longer holds, is a refusal by name. `restore_table` puts a deleted table back.\n\n## A complete workspace\n\nTwo jobs, so two apps: a sales desk, where an order is taken and shipped, and an account review, where an account\nmanager reads each customer by tier. The split is per job, never per table: a table a job reads only in passing, such as\na price book, is a picker on that job\'s lines or an Act of that job\'s app (`first_build`, \xA7 3).\n\nOne `set_tables` call makes both tables: each item carries a `ref`, and a link names the table it points\nat by the `ref` of the other item. Its answer carries each table\'s id. One `set_app` call then makes each\napp, `{ "body": \u2026 }`: the tree\'s root names the app by its `key` and `name`, and the tables it uses in `tables` by those ids,\nshown here as placeholders to replace.\n\n```json\n{\n "tables": [\n {\n "ref": "customer",\n "label": "Customers",\n "singular": "Customer",\n "fields": {\n "name": {"label": "Name", "required": true, "type": "text"},\n "logo": {"label": "Logo", "type": "files"},\n "tier": {\n "label": "Tier",\n "default": ["standard"],\n "type": "select",\n "options": [\n {"alias": "standard", "label": "Standard", "color": "slate"},\n {"alias": "gold", "label": "Gold", "color": "amber"}\n ]\n },\n "orders": {"label": "Orders", "type": "link", "pair": "customer", "table": "order", "multi": true},\n "total_ordered": {"label": "Total ordered", "type": "rollup", "via": "orders", "op": "sum", "field": "amount"}\n },\n "views": [\n {\n "alias": "gold",\n "label": "Gold customers",\n "filters": {\n "node_type": "condition",\n "field_key": "tier",\n "type": "select",\n "operator": "has_any_of",\n "value": ["gold"]\n },\n "sort": [{"field_key": "name", "order": "asc"}]\n }\n ],\n "title": "name",\n "records": {"image": "logo", "party": "organization", "figure": "total_ordered"}\n },\n {\n "ref": "order",\n "label": "Orders",\n "singular": "Order",\n "fields": {\n "code": {"label": "Order no.", "type": "text", "unique": true},\n "placed_on": {"label": "Placed on", "type": "date", "format": "date"},\n "amount": {"label": "Amount", "type": "number", "notation": {"style": "currency", "currency": "VND"}},\n "total": {\n "label": "Total with VAT",\n "type": "formula",\n "expression": "{amount} * 1.1",\n "notation": {"style": "currency", "currency": "VND"}\n },\n "customer": {"label": "Customer", "type": "link", "pair": "orders", "table": "customer", "multi": false},\n "note": {"label": "Note", "type": "text"},\n "stage": {\n "label": "Stage",\n "default": ["draft"],\n "type": "select",\n "options": [\n {"alias": "draft", "label": "Draft", "color": "slate"},\n {"alias": "confirmed", "label": "Confirmed", "color": "blue"},\n {"alias": "shipped", "label": "Shipped", "color": "green"}\n ]\n }\n },\n "title": "customer",\n "records": {\n "subtitle": ["code", "placed_on"],\n "figure": "amount",\n "status": {"field": "stage", "closed": ["shipped"]},\n "starts": {"placed_on": "today"}\n }\n }\n ]\n}\n```\n\n```json\n{\n "body": {\n "type": "App",\n "key": "account_review",\n "name": "Account review",\n "color": "blue",\n "tables": {"customer": "<id of the customer table>", "order": "<id of the order table>"},\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "customer",\n "columns": ["tier"],\n "children": [\n {"type": "Reading", "key": "ordered_by_tier", "by": "tier", "value": "total_ordered"},\n {\n "type": "Record",\n "children": [\n {"type": "Section", "key": "account", "title": "Account", "children": ["tier"]},\n {\n "type": "Section",\n "key": "orders",\n "title": "Orders",\n "children": [{"type": "List", "entity": "order", "columns": ["total"], "create": ["code", "amount"]}]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n```json\n{\n "body": {\n "type": "App",\n "key": "sales_desk",\n "name": "Sales desk",\n "color": "violet",\n "tables": {"customer": "<id of the customer table>", "order": "<id of the order table>"},\n "templates": {},\n "children": [\n {\n "type": "List",\n "entity": "order",\n "filters": ["customer"],\n "create": ["code", "customer", "placed_on"],\n "children": [\n {"type": "Reading", "key": "amount_per_period", "by": "placed_on", "value": "amount"},\n {\n "type": "Record",\n "door": "drawer",\n "children": [\n {\n "type": "Section",\n "key": "order",\n "title": "Order",\n "children": [\n "note",\n {\n "type": "Act",\n "key": "confirm",\n "label": "Confirm",\n "when": {"stage": ["draft"]},\n "set": {"stage": "confirmed"}\n },\n {\n "type": "Act",\n "key": "ship",\n "label": "Ship",\n "when": {"stage": ["confirmed"]},\n "set": {"stage": "shipped"}\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\nThe account review leaves the door a record opens through and the rows it opens in to the system, each the default the\ntable\'s facts give; the sales desk states a drawer. Each List names what its add asks (`create`). The customer\'s orders are a block in its "Orders" section, never its own `Orders`\nlink as a field as well: one fact, one place. An order is titled by its customer and read by its number under it; the\nList names what its add asks, since a subtitle or a figure is never asked by default. `Total ordered` is a rollup, so it\nis read and never asked; the sales desk\'s record opens in a drawer, because its rows are worked one after another. An\norder\'s `stage` is its status (`records.status`), moved only by the app\'s acts: they set it, so the app\'s saves leave it\nout, each act names the stage it is offered at (`when`) and the one it sets, and the register opens on the orders not yet\nshipped (`closed`).\n';
|
|
1765
1765
|
|
|
1766
1766
|
// docs/examples.md
|
|
1767
1767
|
var examples_default = '# Worked examples\n\nEach section is one treatment, cut from a complete workspace of an invented business: the job, the keys to\nread, where the treatment is wrong, and the excerpt \u2014 the app\'s tree, the App node `set_app` takes as `body`,\nwhich the check passes with no finding; the tables it draws as the items of a `set_tables` call, each with its\n`title` and `records`; those tables\' fields it names, by what each holds and how it is drawn; and the templates it makes.\nLeft out, for the page\'s size: a Section that carries nothing of the treatment, every `label` and `description` of a table or field, `required`, `unique`, `default`, a link\'s\n`pair`, how a computed field computes, the `tables` and `templates` maps an App adds (ids from the\n`set_tables` answer), and template contents \u2014 a call states each table\'s and field\'s `label`. Choosing\namong them: the `design` reference. Every key: the `model` reference, at the pages each section names under\n**Keys**.\n\n## Calendar\n\n**The job.** Book visits on the days they happen, and see a week\'s work at a glance.\n\n**Read.** `layout: "calendar"` on the List; each visit stands over its span because its `records` line holds two dates (`scheduled_for`, `ends_at`).\n\n**Keys.** `model/calendar`\n\n**Not when.** Rows booked on a resource that two may not share at once \u2014 that is `lanes`. Rows with a date nobody plans by (an invoice\'s date) stay a table.\n\n```json\n{\n "tables": [\n {\n "ref": "service_visit",\n "title": "customer",\n "fields": {\n "customer": {"type": "link", "table": "customer", "multi": false},\n "scheduled_for": {"type": "date", "format": "datetime"},\n "ends_at": {"type": "date", "format": "datetime"},\n "engineer": {"type": "link", "table": "engineer", "multi": false},\n "job": {\n "type": "select",\n "options": [{"alias": "inspection", "color": "sky"}, {"alias": "repair", "color": "orange"}]\n },\n "stage": {\n "type": "select",\n "options": [\n {"alias": "booked_on", "color": "blue"},\n {"alias": "parts_ready_on", "color": "blue"},\n {"alias": "arrived_on", "color": "blue"},\n {"alias": "completed_on", "color": "green"}\n ]\n },\n "booked_on": {"type": "date"},\n "parts_ready_on": {"type": "date"},\n "arrived_on": {"type": "date"},\n "hours": {"type": "number", "notation": {"style": "unit", "unit": "h"}},\n "report": {"type": "text", "format": "markdown"},\n "completed_on": {"type": "date"}\n },\n "records": {\n "subtitle": ["scheduled_for", "ends_at"],\n "figure": "hours",\n "due": ["scheduled_for"],\n "status": {\n "field": "stage",\n "closed": ["completed_on"],\n "dates": {\n "booked_on": "booked_on",\n "parts_ready_on": "parts_ready_on",\n "arrived_on": "arrived_on",\n "completed_on": "completed_on"\n }\n }\n }\n }\n ],\n "body": {\n "type": "App",\n "key": "visits",\n "name": "Service visits",\n "icon": "calendar",\n "color": "emerald",\n "children": [\n {\n "type": "List",\n "entity": "service_visit",\n "row": {"title": {"add": ["name", "credit_limit"]}},\n "columns": ["engineer", "job"],\n "filters": ["customer"],\n "sort": {"field": "scheduled_for"},\n "layout": "calendar",\n "create": [\n "customer",\n "job",\n "scheduled_for",\n "ends_at",\n {"field": "engineer", "add": ["name", {"field": "base", "add": ["name", "city"]}]}\n ],\n "children": [\n {"type": "Reading", "key": "register_reading_0", "label": "Repairs", "where": {"job": ["repair"]}},\n {"type": "Reading", "key": "register_reading_1", "value": "hours", "by": "engineer"},\n {\n "type": "Record",\n "children": [\n {\n "type": "Section",\n "key": "visit",\n "title": "Visit",\n "at": ["booked_on", "parts_ready_on"],\n "children": [\n "job",\n "booked_on",\n "parts_ready_on",\n {\n "type": "Act",\n "key": "reschedule",\n "label": "Reschedule",\n "asks": ["scheduled_for", "ends_at"],\n "when": {"stage": ["booked_on", "parts_ready_on"]}\n },\n {\n "type": "Act",\n "key": "to_booked_on",\n "label": "Booked",\n "when": {"stage": {"empty": true}},\n "asks": [{"field": "booked_on", "starts": "today"}],\n "set": {"stage": "booked_on"}\n },\n {\n "type": "Act",\n "key": "to_parts_ready_on",\n "label": "Parts ready",\n "when": {"stage": ["booked_on"], "job": ["repair"]},\n "asks": [{"field": "parts_ready_on", "starts": "today"}],\n "set": {"stage": "parts_ready_on"}\n },\n {\n "type": "Act",\n "key": "to_arrived_on",\n "label": "On site",\n "when": {"stage": ["booked_on"], "job": ["inspection"]},\n "asks": [{"field": "arrived_on", "starts": "today"}],\n "set": {"stage": "arrived_on"}\n },\n {\n "type": "Act",\n "key": "to_arrived_on_2",\n "label": "On site",\n "when": {"stage": ["parts_ready_on"]},\n "asks": [{"field": "arrived_on", "starts": "today"}],\n "set": {"stage": "arrived_on"}\n }\n ]\n },\n {\n "type": "Section",\n "key": "report",\n "title": "Report",\n "at": ["arrived_on"],\n "children": [\n "engineer",\n "arrived_on",\n {"field": "report", "block": false},\n {\n "type": "Act",\n "key": "complete",\n "label": "Complete visit",\n "requires": ["engineer"],\n "asks": [\n "hours",\n {"input": "summary", "label": "What was done", "type": "long_text", "required": true}\n ],\n "set": {"report": {"input": "summary"}, "stage": "completed_on"},\n "when": {"stage": ["arrived_on"]}\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Lanes\n\n**The job.** Put each service visit on an engineer\'s day by the hour, and see at once who is free between visits.\n\n**Read.** `layout` `lanes` with `by` naming the one-row link to the resource (the engineer); every engineer stands as a lane, empty or not.\n\n**Keys.** `model/lanes`\n\n**Not when.** The resource is not a row of the model (a free-text room name), or rows never compete for one \u2014 a `calendar` reads them by day.\n\n```json\n{\n "tables": [\n {\n "ref": "service_visit",\n "title": "customer",\n "fields": {\n "customer": {"type": "link", "table": "customer", "multi": false},\n "scheduled_for": {"type": "date", "format": "datetime"},\n "ends_at": {"type": "date", "format": "datetime"},\n "engineer": {"type": "link", "table": "engineer", "multi": false},\n "job": {\n "type": "select",\n "options": [{"alias": "inspection", "color": "sky"}, {"alias": "repair", "color": "orange"}]\n },\n "stage": {\n "type": "select",\n "options": [\n {"alias": "booked_on", "color": "blue"},\n {"alias": "parts_ready_on", "color": "blue"},\n {"alias": "arrived_on", "color": "blue"},\n {"alias": "completed_on", "color": "green"}\n ]\n },\n "booked_on": {"type": "date"},\n "parts_ready_on": {"type": "date"},\n "arrived_on": {"type": "date"},\n "hours": {"type": "number", "notation": {"style": "unit", "unit": "h"}},\n "completed_on": {"type": "date"}\n },\n "records": {\n "subtitle": ["scheduled_for", "ends_at"],\n "figure": "hours",\n "due": ["scheduled_for"],\n "status": {\n "field": "stage",\n "closed": ["completed_on"],\n "dates": {\n "booked_on": "booked_on",\n "parts_ready_on": "parts_ready_on",\n "arrived_on": "arrived_on",\n "completed_on": "completed_on"\n }\n }\n }\n },\n {\n "ref": "engineer",\n "title": "name",\n "fields": {"name": {"type": "text"}, "base": {"type": "link", "table": "warehouse", "multi": false}},\n "records": {"subtitle": ["base"], "party": "person"}\n }\n ],\n "body": {\n "type": "App",\n "key": "dispatch",\n "name": "Dispatch",\n "icon": "wrench",\n "color": "sky",\n "children": [\n {\n "type": "List",\n "entity": "service_visit",\n "row": {\n "title": {"add": ["name", "credit_limit"]},\n "subtitle": [{"field": "scheduled_for", "edit": false}, {"field": "ends_at", "edit": false}]\n },\n "columns": ["job"],\n "search": ["customer"],\n "layout": {"lanes": {"by": "engineer"}},\n "create": [\n {"field": "engineer", "add": ["name", {"field": "base", "add": ["name", "city"]}]},\n "scheduled_for",\n "ends_at",\n "customer",\n "job"\n ],\n "children": [\n {\n "type": "Record",\n "door": "drawer",\n "children": [\n {\n "type": "Section",\n "key": "booking",\n "title": "Booking",\n "children": [\n "engineer",\n "job",\n {\n "type": "Act",\n "key": "to_booked_on",\n "label": "Booked",\n "when": {"stage": {"empty": true}},\n "asks": [{"field": "booked_on", "starts": "today"}],\n "set": {"stage": "booked_on"}\n },\n {\n "type": "Act",\n "key": "to_parts_ready_on",\n "label": "Parts ready",\n "when": {"stage": ["booked_on"], "job": ["repair"]},\n "asks": [{"field": "parts_ready_on", "starts": "today"}],\n "set": {"stage": "parts_ready_on"}\n },\n {\n "type": "Act",\n "key": "to_arrived_on",\n "label": "On site",\n "when": {"stage": ["booked_on"], "job": ["inspection"]},\n "asks": [{"field": "arrived_on", "starts": "today"}],\n "set": {"stage": "arrived_on"}\n },\n {\n "type": "Act",\n "key": "to_arrived_on_2",\n "label": "On site",\n "when": {"stage": ["parts_ready_on"]},\n "asks": [{"field": "arrived_on", "starts": "today"}],\n "set": {"stage": "arrived_on"}\n },\n {\n "type": "Act",\n "key": "to_completed_on",\n "label": "Completed",\n "when": {"stage": ["arrived_on"]},\n "asks": [{"field": "completed_on", "starts": "today"}],\n "set": {"stage": "completed_on"}\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Gantt\n\n**The job.** Read every hire as a bar from the day the machine goes out to the day it is due back, against today.\n\n**Read.** `layout` `gantt` with its `start`; with no end stated, the bar ends at the entity\'s first `due`. The day a hire comes back (`ngay_tra`) is stamped by the status\'s `dates`, so no act sets it.\n\n**Keys.** `model/gantt`\n\n**Not when.** Rows that last a day or less (a visit, a shift) \u2014 `calendar` or `lanes`; a plan with no dates set yet.\n\n```json\n{\n "tables": [\n {\n "ref": "phieu_thue",\n "title": "khach_hang",\n "fields": {\n "khach_hang": {"type": "link", "table": "khach_hang", "multi": false},\n "so_phieu": {"type": "text"},\n "trang_thai": {\n "type": "select",\n "options": [\n {"alias": "moi", "color": "slate"},\n {"alias": "dang_thue", "color": "blue"},\n {"alias": "da_tra", "color": "green"},\n {"alias": "huy", "color": "gray"}\n ]\n },\n "phu_trach": {"type": "member"},\n "ngay_giao": {"type": "date"},\n "han_tra": {"type": "date"},\n "ngay_tra": {"type": "date"},\n "tinh_trang_tra": {\n "type": "select",\n "options": [\n {"alias": "tot", "color": "green"},\n {"alias": "tray_xuoc", "color": "amber"},\n {"alias": "hu_hong", "color": "red"}\n ]\n },\n "so_ngay": {"type": "formula"},\n "tong_tien": {"type": "rollup"}\n },\n "records": {\n "subtitle": ["so_phieu"],\n "figure": "tong_tien",\n "due": ["han_tra"],\n "status": {"field": "trang_thai", "closed": ["da_tra", "huy"], "dates": {"da_tra": "ngay_tra"}}\n }\n }\n ],\n "body": {\n "type": "App",\n "key": "tien_do_thue",\n "name": "Ti\u1EBFn \u0111\u1ED9 cho thu\xEA",\n "icon": "calendar-range",\n "color": "teal",\n "children": [\n {\n "type": "List",\n "entity": "phieu_thue",\n "row": {"title": {"add": true}},\n "columns": ["phu_trach"],\n "filters": ["phu_trach", "khach_hang"],\n "layout": {"gantt": {"start": "ngay_giao"}},\n "create": false,\n "remove": false,\n "children": [\n {\n "type": "Reading",\n "key": "register_reading_0",\n "label": "Phi\u1EBFu \u0111ang cho thu\xEA",\n "where": {"trang_thai": ["dang_thue"]}\n },\n {\n "type": "Reading",\n "key": "register_reading_1",\n "label": "Ti\u1EC1n thu\xEA \u0111ang ch\u1EA1y",\n "value": "tong_tien",\n "where": {"trang_thai": ["dang_thue"]}\n },\n {\n "type": "Record",\n "children": [\n {\n "type": "Section",\n "key": "phieu_thue",\n "title": "Phi\u1EBFu thu\xEA",\n "children": ["ngay_giao", "han_tra"]\n },\n {\n "type": "Section",\n "key": "dang_thue",\n "title": "\u0110ang thu\xEA",\n "at": ["dang_thue"],\n "children": [\n "so_ngay",\n "ngay_tra",\n {\n "type": "Act",\n "key": "nhan_tra",\n "label": "Nh\u1EADn m\xE1y tr\u1EA3",\n "when": {"trang_thai": ["dang_thue"]},\n "asks": ["tinh_trang_tra"],\n "set": {"trang_thai": "da_tra"},\n "confirm": true\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Roster\n\n**The job.** See who works which shift on each day of the week or the month.\n\n**Read.** `layout: "roster"` over the people; the child List (`lich_truc`) is the rows that fill the days \u2014 one link back and a date on its line.\n\n**Keys.** `model/roster`\n\n**Not when.** Rows that are themselves the bookings (a stay over several days) \u2014 a `calendar` or `lanes` draws them; a roster\'s row is what the days are filled for.\n\n```json\n{\n "tables": [\n {\n "ref": "nha_si",\n "title": "ho_ten",\n "fields": {\n "ho_ten": {"type": "text"},\n "chuyen_mon": {\n "type": "select",\n "options": [\n {"alias": "tong_quat", "color": "blue"},\n {"alias": "chinh_nha", "color": "purple"},\n {"alias": "nha_chu", "color": "green"},\n {"alias": "phuc_hinh", "color": "orange"}\n ]\n },\n "dien_thoai": {"type": "text"}\n },\n "records": {"subtitle": ["chuyen_mon"], "party": "person"}\n },\n {\n "ref": "lich_truc",\n "title": "ca",\n "fields": {\n "nha_si": {"type": "link", "table": "nha_si", "multi": false},\n "ca": {\n "type": "select",\n "options": [\n {"alias": "sang", "color": "blue"},\n {"alias": "chieu", "color": "purple"},\n {"alias": "ca_ngay", "color": "green"},\n {"alias": "nghi", "color": "gray"}\n ]\n },\n "ngay": {"type": "date"},\n "ghe": {"type": "link", "table": "ghe", "multi": false}\n },\n "records": {"subtitle": ["ngay", "ghe"]}\n }\n ],\n "body": {\n "type": "App",\n "key": "lich_truc",\n "name": "L\u1ECBch tr\u1EF1c",\n "icon": "users",\n "color": "indigo",\n "children": [\n {\n "type": "List",\n "entity": "nha_si",\n "filters": ["chuyen_mon"],\n "layout": "roster",\n "create": ["ho_ten", "chuyen_mon", "dien_thoai"],\n "children": [\n {\n "type": "List",\n "entity": "lich_truc",\n "row": {"subtitle": ["ngay", {"field": "ghe", "add": ["ten", "phong"]}]},\n "create": true,\n "remove": true\n }\n ]\n }\n ]\n }\n}\n```\n\n## Cards\n\n**The job.** Find an item by its picture, and count what the shelf holds.\n\n**Read.** `layout: "cards"` over rows whose `records.image` is a photo; `scope` keeps the app inside one warehouse at a time.\n\n**Keys.** `model/cards`, `model/records`, `model/app-node`\n\n**Not when.** Rows with no picture \u2014 cards of words are a table drawn worse.\n\n```json\n{\n "tables": [\n {\n "ref": "stock_item",\n "title": "name",\n "fields": {\n "name": {"type": "text"},\n "warehouse": {"type": "link", "table": "warehouse", "multi": false},\n "photo": {"type": "files"},\n "sku": {"type": "text"},\n "category": {\n "type": "select",\n "options": [\n {"alias": "pumps", "color": "blue"},\n {"alias": "valves", "color": "teal"},\n {"alias": "seals", "color": "amber"},\n {"alias": "motors", "color": "violet"}\n ]\n },\n "on_hand": {"type": "number"},\n "minimum": {"type": "number"},\n "price": {"type": "number", "notation": {"style": "currency", "currency": "USD"}},\n "supplier_page": {"type": "text", "format": "link"},\n "last_counted": {"type": "date"},\n "counted_by": {"type": "member"},\n "count_note": {"type": "text"}\n },\n "records": {\n "subtitle": ["sku", "category"],\n "image": "photo",\n "figure": "on_hand",\n "limits": {"on_hand": 400},\n "gates": {"on_hand": "minimum"}\n }\n },\n {\n "ref": "order_line",\n "title": "item",\n "fields": {\n "item": {"type": "link", "table": "stock_item", "multi": false},\n "order": {"type": "link", "table": "order", "multi": false},\n "quantity": {"type": "number"},\n "shipped": {"type": "number"},\n "picked": {"type": "number"},\n "unit_price": {"type": "number", "notation": {"style": "currency", "currency": "USD"}},\n "amount": {"type": "formula", "notation": {"style": "currency", "currency": "USD"}},\n "settled": {"type": "formula"},\n "category": {"type": "lookup"}\n },\n "records": {\n "subtitle": ["order", "unit_price"],\n "figure": "amount",\n "limits": {"shipped": "quantity", "picked": "quantity"}\n }\n }\n ],\n "body": {\n "type": "App",\n "key": "stock",\n "name": "Stock",\n "icon": "boxes",\n "color": "teal",\n "scope": "warehouse",\n "children": [\n {\n "type": "List",\n "entity": "stock_item",\n "columns": ["price"],\n "filters": ["on_hand"],\n "search": ["name", "sku"],\n "layout": "cards",\n "children": [\n {"type": "Reading", "key": "register_reading_0", "value": "on_hand", "by": "category"},\n {"type": "Export", "key": "export"},\n {\n "type": "Record",\n "children": [\n {\n "type": "Section",\n "key": "item",\n "title": "Item",\n "children": ["price", "minimum", "supplier_page"]\n },\n {\n "type": "Section",\n "key": "last_count",\n "title": "Last count",\n "children": [\n "last_counted",\n "counted_by",\n "count_note",\n {\n "type": "Act",\n "key": "count",\n "label": "Count stock",\n "asks": [\n {"input": "counted", "label": "Units on the shelf", "type": "number", "required": true},\n {"input": "remark", "label": "Remark", "type": "long_text"}\n ],\n "set": {\n "on_hand": {"input": "counted"},\n "last_counted": {"now": true},\n "counted_by": {"me": true},\n "count_note": {"input": "remark"}\n }\n }\n ]\n },\n {\n "type": "Section",\n "key": "on_order",\n "title": "On order",\n "children": [\n {\n "type": "List",\n "entity": "order_line",\n "row": {\n "subtitle": [\n {\n "field": "order",\n "add": [{"field": "customer", "add": ["name", "credit_limit"]}, "reference"]\n },\n "unit_price"\n ]\n },\n "columns": ["quantity"],\n "children": [\n {"type": "Check", "field": "settled", "on": ["update"]},\n {"type": "Record", "children": ["item", "order", "quantity", "shipped", "unit_price"]}\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Party\n\n**The job.** Keep each customer account \u2014 who they are, what they ordered, what was said to them.\n\n**Read.** `records.customer.party: "organization"` and its `image` (the logo); a List with `layout: "timeline"` for the touches, a Reading `by` the day placed for its orders, and its papers `expect`ed by kind.\n\n**Keys.** `model/records`, `model/layouts`, `model/list-node`, `model/reading-node`\n\n**Not when.** A list of customers is not a job by itself: the customer is a link on the desk whose rows name it, unless the job is the account itself.\n\n```json\n{\n "tables": [\n {\n "ref": "customer",\n "title": "name",\n "fields": {\n "name": {"type": "text"},\n "logo": {"type": "files"},\n "code": {"type": "text"},\n "tier": {\n "type": "select",\n "options": [\n {"alias": "key", "color": "violet"},\n {"alias": "standard", "color": "sky"},\n {"alias": "lapsed", "color": "gray"}\n ]\n },\n "phone": {"type": "text"},\n "website": {"type": "text", "format": "link"},\n "account_manager": {"type": "member"},\n "credit_limit": {"type": "number", "notation": {"style": "currency", "currency": "USD"}},\n "orders": {"type": "link", "table": "order", "multi": true},\n "outstanding": {"type": "rollup"},\n "over_limit": {"type": "formula"},\n "papers_needed": {\n "type": "select",\n "options": [\n {"alias": "credit_application", "color": "blue"},\n {"alias": "trade_reference", "color": "violet"},\n {"alias": "vat_certificate", "color": "teal"}\n ],\n "multi": true\n },\n "notes": {"type": "text", "format": "markdown"}\n },\n "records": {\n "subtitle": ["tier"],\n "image": "logo",\n "party": "organization",\n "figure": "outstanding",\n "limits": {"outstanding": "credit_limit"}\n }\n },\n {\n "ref": "order",\n "title": "reference",\n "fields": {\n "customer": {"type": "link", "table": "customer", "multi": false},\n "reference": {"type": "text"},\n "stage": {\n "type": "select",\n "options": [\n {"alias": "draft", "color": "slate"},\n {"alias": "confirmed", "color": "blue"},\n {"alias": "picking", "color": "amber"},\n {"alias": "shipped", "color": "indigo"},\n {"alias": "received", "color": "green"},\n {"alias": "cancelled", "color": "rose"}\n ]\n },\n "placed": {"type": "date"},\n "due_date": {"type": "date"},\n "handler": {"type": "member"},\n "priority": {"type": "boolean"},\n "lines": {"type": "link", "table": "order_line", "multi": true},\n "payments": {"type": "link", "table": "payment", "multi": true},\n "total": {"type": "rollup"},\n "balance": {"type": "formula", "notation": {"style": "currency", "currency": "USD"}},\n "units_ordered": {"type": "rollup"},\n "units_shipped": {"type": "rollup"},\n "units_picked": {"type": "rollup"},\n "carrier": {"type": "text"},\n "tracking_no": {"type": "text"},\n "shipped_on": {"type": "date"},\n "cancellation_reason": {"type": "text"},\n "cancelled_by": {"type": "member"},\n "replaced_by": {"type": "link", "table": "order", "multi": false},\n "attachments": {"type": "files"},\n "delivery_note": {"type": "files"},\n "notes": {"type": "text", "format": "markdown"},\n "paid_in_full": {"type": "formula"},\n "has_payments": {"type": "formula"},\n "reminder_ref": {"type": "text"}\n },\n "records": {\n "subtitle": ["customer", "placed"],\n "figure": "total",\n "limits": {"units_shipped": "units_ordered", "units_picked": "units_ordered"},\n "due": ["due_date"],\n "status": {\n "field": "stage",\n "closed": ["received", "cancelled"],\n "dates": {"shipped": "shipped_on"},\n "history": "order_stage"\n }\n }\n },\n {\n "ref": "customer_paper",\n "title": "kind",\n "fields": {\n "kind": {\n "type": "select",\n "options": [\n {"alias": "credit_application", "color": "blue"},\n {"alias": "trade_reference", "color": "violet"},\n {"alias": "vat_certificate", "color": "teal"}\n ]\n },\n "customer": {"type": "link", "table": "customer", "multi": false},\n "received_on": {"type": "date"},\n "file": {"type": "files"}\n },\n "records": {"image": "file"}\n },\n {\n "ref": "touch",\n "title": "said",\n "fields": {\n "said": {"type": "text"},\n "customer": {"type": "link", "table": "customer", "multi": false},\n "on": {"type": "date"},\n "by": {"type": "member"}\n }\n },\n {\n "ref": "service_visit",\n "title": "customer",\n "fields": {\n "customer": {"type": "link", "table": "customer", "multi": false},\n "scheduled_for": {"type": "date", "format": "datetime"},\n "ends_at": {"type": "date", "format": "datetime"},\n "engineer": {"type": "link", "table": "engineer", "multi": false},\n "job": {\n "type": "select",\n "options": [{"alias": "inspection", "color": "sky"}, {"alias": "repair", "color": "orange"}]\n },\n "stage": {\n "type": "select",\n "options": [\n {"alias": "booked_on", "color": "blue"},\n {"alias": "parts_ready_on", "color": "blue"},\n {"alias": "arrived_on", "color": "blue"},\n {"alias": "completed_on", "color": "green"}\n ]\n },\n "booked_on": {"type": "date"},\n "parts_ready_on": {"type": "date"},\n "arrived_on": {"type": "date"},\n "hours": {"type": "number", "notation": {"style": "unit", "unit": "h"}},\n "report": {"type": "text", "format": "markdown"},\n "completed_on": {"type": "date"}\n },\n "records": {\n "subtitle": ["scheduled_for", "ends_at"],\n "figure": "hours",\n "due": ["scheduled_for"],\n "status": {\n "field": "stage",\n "closed": ["completed_on"],\n "dates": {\n "booked_on": "booked_on",\n "parts_ready_on": "parts_ready_on",\n "arrived_on": "arrived_on",\n "completed_on": "completed_on"\n }\n }\n }\n }\n ],\n "body": {\n "type": "App",\n "key": "customers",\n "name": "Customers",\n "icon": "users",\n "color": "violet",\n "children": [\n {\n "type": "List",\n "entity": "customer",\n "columns": ["phone", "account_manager"],\n "filters": ["account_manager"],\n "sort": {"field": "name"},\n "opens": {"tier": ["key", "standard"]},\n "create": ["name", "code", "credit_limit"],\n "children": [\n {"type": "Reading", "key": "register_reading_0", "value": "outstanding", "by": "tier"},\n {"type": "Reading", "key": "register_reading_1", "value": "outstanding", "by": "account_manager"},\n {"type": "Check", "field": "over_limit"},\n {\n "type": "Record",\n "door": "drawer",\n "children": [\n {"type": "Section", "key": "contact", "title": "Contact", "children": ["phone", "website"]},\n {\n "type": "Section",\n "key": "account",\n "title": "Account",\n "children": [\n "code",\n "account_manager",\n "credit_limit",\n {\n "type": "Act",\n "key": "reassign",\n "label": "Reassign",\n "asks": [{"input": "to", "label": "Account manager", "type": "member", "required": true}],\n "set": {"account_manager": {"input": "to"}}\n },\n {\n "type": "Act",\n "key": "lapse",\n "label": "Mark lapsed",\n "when": {"tier": ["key", "standard"]},\n "set": {"tier": "lapsed"},\n "confirm": true\n },\n {\n "type": "Act",\n "key": "reinstate",\n "label": "Reinstate",\n "when": {"tier": ["lapsed"]},\n "asks": [\n {\n "input": "back_to",\n "label": "Back to",\n "type": "select",\n "options": [\n {"alias": "key", "label": "Key account", "color": "violet"},\n {"alias": "standard", "label": "Standard", "color": "sky"}\n ],\n "required": true\n }\n ],\n "set": {"tier": {"input": "back_to"}}\n }\n ]\n },\n {\n "type": "Section",\n "key": "orders",\n "title": "Orders",\n "children": [\n {\n "type": "Reading",\n "key": "block_reading_0",\n "label": "Order value",\n "entity": "order",\n "value": "total",\n "where": {"stage": ["confirmed", "picking", "shipped", "received"]},\n "by": "placed"\n },\n {\n "type": "List",\n "entity": "order",\n "where": {"stage": ["draft", "confirmed", "picking", "shipped"]},\n "label": "Open orders",\n "columns": ["due_date"],\n "create": false,\n "children": [\n {"type": "Check", "field": "paid_in_full", "on": ["update"]},\n {"type": "Check", "field": "has_payments"},\n {\n "type": "Record",\n "children": [\n "due_date",\n "handler",\n {"field": "priority", "edit": false},\n {\n "field": "lines",\n "add": [\n {\n "field": "item",\n "add": ["name", {"field": "warehouse", "add": ["name", "city"]}, "sku", "category"]\n },\n "quantity"\n ]\n },\n {"field": "payments", "add": true},\n {"field": "carrier", "edit": false},\n {"field": "tracking_no", "edit": false},\n {"field": "shipped_on", "edit": false},\n {"field": "cancellation_reason", "edit": false},\n {"field": "cancelled_by", "edit": false},\n {"field": "replaced_by", "edit": false},\n {"field": "attachments", "edit": false},\n "delivery_note",\n "notes",\n {"field": "reminder_ref", "edit": false}\n ]\n }\n ]\n },\n {\n "type": "List",\n "key": "order_2",\n "entity": "order",\n "where": {"stage": ["shipped", "received"], "paid_in_full": false},\n "label": "Shipped, still owed",\n "columns": ["balance"],\n "create": false,\n "children": [{"type": "Record", "children": ["due_date"]}]\n },\n {\n "type": "List",\n "key": "block_reading_3",\n "entity": "service_visit",\n "app": "visits",\n "label": "Service visits",\n "columns": ["engineer", "job"],\n "children": [\n {\n "type": "Act",\n "key": "clear_visit_hours",\n "label": "Clear hours",\n "set": {"hours": null},\n "confirm": true\n },\n {"type": "Record", "edit": false, "children": ["report", "arrived_on", "completed_on"]}\n ]\n }\n ]\n },\n {\n "type": "Section",\n "key": "account_papers",\n "title": "Account papers",\n "children": [\n {\n "type": "List",\n "entity": "customer_paper",\n "expect": {"field": "kind", "of": "papers_needed"},\n "columns": ["received_on"]\n }\n ]\n },\n {\n "type": "Section",\n "key": "contact_log",\n "title": "Contact log",\n "children": [{"type": "List", "entity": "touch", "layout": "timeline"}]\n },\n {"type": "Section", "key": "notes", "title": "Notes", "children": ["notes"]}\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Case\n\n**The job.** Gather an applicant\'s papers into one case, make the forms it needs, and file it on time.\n\n**Read.** Lists with `expect` for the papers brought (one line per kind, the missing ones empty), an Act\'s `templates` for the forms made (ticked and made at once, kept in `into`), and `intake` reading the papers brought.\n\n**Keys.** `model/list-node`, `model/act-node`\n\n**Not when.** A record that needs no papers, or one paper made once \u2014 a single `template` act.\n\n```json\n{\n "tables": [\n {\n "ref": "ho_so",\n "title": "khach_hang",\n "fields": {\n "khach_hang": {"type": "link", "table": "khach_hang", "multi": false},\n "du_an": {"type": "link", "table": "du_an", "multi": false},\n "ma_ho_so": {"type": "autonumber"},\n "dich_vu": {\n "type": "select",\n "options": [{"alias": "tron_goi", "color": "blue"}, {"alias": "lam_ho_so", "color": "violet"}]\n },\n "doi_tuong": {\n "type": "select",\n "options": [\n {"alias": "thu_nhap_thap", "color": "sky"},\n {"alias": "cong_nhan", "color": "teal"},\n {"alias": "can_bo", "color": "indigo"},\n {"alias": "luc_luong", "color": "green"},\n {"alias": "ho_ngheo", "color": "amber"},\n {"alias": "nguoi_co_cong", "color": "rose"}\n ]\n },\n "tinh_trang_nha_o": {\n "type": "select",\n "options": [{"alias": "chua_co_nha", "color": "gray"}, {"alias": "nha_chat", "color": "orange"}]\n },\n "giay_chung_nhan_so": {"type": "text"},\n "dien_tich_nha": {"type": "number"},\n "sale": {"type": "member"},\n "xu_ly": {"type": "member"},\n "can_bo_sung": {"type": "boolean"},\n "phi_dich_vu": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},\n "loai_can": {\n "type": "select",\n "options": [\n {"alias": "studio", "color": "sky"},\n {"alias": "mot_pn", "color": "blue"},\n {"alias": "hai_pn", "color": "indigo"},\n {"alias": "ba_pn", "color": "violet"}\n ]\n },\n "giai_doan": {\n "type": "select",\n "options": [\n {"alias": "ngay_thu_phi", "color": "blue"},\n {"alias": "ngay_nhan_giay_to", "color": "blue"},\n {"alias": "ngay_lap_ho_so", "color": "blue"},\n {"alias": "ngay_nop_cdt", "color": "blue"},\n {"alias": "ngay_cdt_duyet", "color": "blue"},\n {"alias": "ngay_so_xd_duyet", "color": "blue"},\n {"alias": "ngay_boc_tham", "color": "blue"},\n {"alias": "ngay_ky_hdmb", "color": "green"},\n {"alias": "ngay_ban_giao", "color": "green"}\n ]\n },\n "ngay_thu_phi": {"type": "date"},\n "ngay_nhan_giay_to": {"type": "date"},\n "ngay_lap_ho_so": {"type": "date"},\n "ngay_nop_cdt": {"type": "date"},\n "ngay_cdt_duyet": {"type": "date"},\n "ngay_so_xd_duyet": {"type": "date"},\n "ngay_boc_tham": {"type": "date"},\n "ngay_ky_hdmb": {"type": "date"},\n "ngay_ban_giao": {"type": "date"},\n "bo_ho_so": {"type": "files"},\n "giay_to": {"type": "link", "table": "giay_to", "multi": true},\n "thanh_vien": {"type": "link", "table": "thanh_vien", "multi": true},\n "phieu_thu": {"type": "link", "table": "phieu_thu", "multi": true},\n "dien_thoai": {"type": "lookup"},\n "cccd": {"type": "lookup"},\n "ngay_sinh": {"type": "lookup"},\n "dia_chi": {"type": "lookup"},\n "da_thu": {"type": "rollup"},\n "chua_thu_phi": {"type": "formula"},\n "con_no_phi": {"type": "formula"},\n "vuot_thu_nhap": {"type": "formula"},\n "da_hoan_tat": {"type": "formula"}\n },\n "records": {\n "subtitle": ["dich_vu"],\n "figure": "phi_dich_vu",\n "status": {\n "field": "giai_doan",\n "closed": ["ngay_ky_hdmb", "ngay_ban_giao"],\n "dates": {\n "ngay_thu_phi": "ngay_thu_phi",\n "ngay_nhan_giay_to": "ngay_nhan_giay_to",\n "ngay_lap_ho_so": "ngay_lap_ho_so",\n "ngay_nop_cdt": "ngay_nop_cdt",\n "ngay_cdt_duyet": "ngay_cdt_duyet",\n "ngay_so_xd_duyet": "ngay_so_xd_duyet",\n "ngay_boc_tham": "ngay_boc_tham",\n "ngay_ky_hdmb": "ngay_ky_hdmb",\n "ngay_ban_giao": "ngay_ban_giao"\n }\n }\n }\n },\n {\n "ref": "thanh_vien",\n "title": "ho_ten",\n "fields": {\n "ho_ten": {"type": "text"},\n "ho_so": {"type": "link", "table": "ho_so", "multi": false},\n "quan_he": {\n "type": "select",\n "options": [\n {"alias": "nguoi_dung_don", "color": "blue"},\n {"alias": "vo_chong", "color": "violet"},\n {"alias": "con", "color": "teal"},\n {"alias": "bo_me", "color": "amber"},\n {"alias": "khac", "color": "gray"}\n ]\n },\n "ngay_sinh": {"type": "date"},\n "cccd": {"type": "text"},\n "thu_nhap": {"type": "number", "notation": {"style": "currency", "currency": "VND"}}\n },\n "records": {"subtitle": ["quan_he", "cccd"], "party": "person", "figure": "thu_nhap"}\n },\n {\n "ref": "giay_to",\n "title": "loai",\n "fields": {\n "loai": {\n "type": "select",\n "options": [\n {"alias": "don_dang_ky", "color": "blue"},\n {"alias": "can_cuoc", "color": "sky"},\n {"alias": "giay_doi_tuong", "color": "violet"},\n {"alias": "xac_nhan_nha_o", "color": "teal"},\n {"alias": "xac_nhan_thu_nhap", "color": "amber"},\n {"alias": "hon_nhan", "color": "indigo"}\n ]\n },\n "ho_so": {"type": "link", "table": "ho_so", "multi": false},\n "ngay_nhan": {"type": "date"},\n "tep": {"type": "files"},\n "phai_sua": {"type": "formula"}\n },\n "records": {"image": "tep", "starts": {"ngay_nhan": "today"}}\n },\n {\n "ref": "phieu_thu",\n "title": "khoan",\n "fields": {\n "khoan": {\n "type": "select",\n "options": [\n {"alias": "dat_coc", "color": "blue"},\n {"alias": "tat_toan", "color": "indigo"},\n {"alias": "phi_ho_so", "color": "violet"}\n ]\n },\n "ho_so": {"type": "link", "table": "ho_so", "multi": false},\n "so_tien": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},\n "hinh_thuc": {\n "type": "select",\n "options": [\n {"alias": "tien_mat", "color": "green", "mark": {"kind": "icon", "name": "banknote"}},\n {"alias": "chuyen_khoan", "color": "blue", "mark": {"kind": "icon", "name": "arrow-right-left"}}\n ]\n },\n "trang_thai": {\n "type": "select",\n "options": [\n {"alias": "cho_thu", "color": "amber"},\n {"alias": "da_thu", "color": "green"},\n {"alias": "huy", "color": "gray"}\n ]\n },\n "ngay_thu": {"type": "date"}\n },\n "records": {\n "subtitle": ["hinh_thuc", "ngay_thu"],\n "figure": "so_tien",\n "status": {"field": "trang_thai", "closed": ["da_thu", "huy"]}\n }\n }\n ],\n "templates": [\n {"alias": "don_dang_ky", "type": "html", "label": "\u0110\u01A1n \u0111\u0103ng k\xFD mua nh\xE0 \u1EDF x\xE3 h\u1ED9i"},\n {"alias": "mau_02_chua_co_nha", "type": "html", "label": "M\u1EABu 02 \u2014 X\xE1c nh\u1EADn ch\u01B0a c\xF3 nh\xE0 \u1EDF"},\n {"alias": "mau_03_nha_chat", "type": "html", "label": "M\u1EABu 03 \u2014 X\xE1c nh\u1EADn nh\xE0 \u1EDF d\u01B0\u1EDBi 15 m\xB2/ng\u01B0\u1EDDi"},\n {"alias": "mau_05_thu_nhap", "type": "html", "label": "M\u1EABu 05 \u2014 X\xE1c nh\u1EADn thu nh\u1EADp"},\n {"alias": "danh_sach_nop", "type": "html", "label": "Danh s\xE1ch h\u1ED3 s\u01A1 n\u1ED9p ch\u1EE7 \u0111\u1EA7u t\u01B0"}\n ],\n "body": {\n "type": "App",\n "key": "ho_so",\n "name": "H\u1ED3 s\u01A1 nh\xE0 \u1EDF x\xE3 h\u1ED9i",\n "icon": "folder-open",\n "color": "blue",\n "scope": "du_an",\n "children": [\n {\n "type": "List",\n "entity": "ho_so",\n "row": {"title": {"add": true}},\n "columns": ["xu_ly", "can_bo_sung", "doi_tuong"],\n "filters": ["xu_ly", "can_bo_sung"],\n "search": ["khach_hang", "ma_ho_so", "dien_thoai"],\n "create": ["khach_hang", "dich_vu", "doi_tuong", "tinh_trang_nha_o", "loai_can", "phi_dich_vu", "sale"],\n "children": [\n {\n "type": "Reading",\n "key": "register_reading_0",\n "label": "Ch\u1EDD kh\xE1ch b\u1ED5 sung",\n "where": {"can_bo_sung": true}\n },\n {\n "type": "Reading",\n "key": "register_reading_1",\n "label": "Ph\xED \u0111\xE3 thu",\n "value": "da_thu",\n "over": "ngay_nhan_giay_to"\n },\n {"type": "Reading", "key": "register_reading_2", "by": "doi_tuong"},\n {"type": "Export", "key": "export"},\n {"type": "Check", "field": "chua_thu_phi", "on": ["nop_cdt"]},\n {"type": "Check", "field": "con_no_phi"},\n {"type": "Check", "field": "vuot_thu_nhap"},\n {"type": "Check", "field": "da_hoan_tat", "on": ["doc_giay_to"]},\n {\n "type": "Record",\n "children": [\n {\n "type": "Section",\n "key": "nguoi_dung_don",\n "title": "Ng\u01B0\u1EDDi \u0111\u1EE9ng \u0111\u01A1n",\n "children": [\n "ma_ho_so",\n "dien_thoai",\n "cccd",\n "ngay_sinh",\n "dia_chi",\n "doi_tuong",\n "tinh_trang_nha_o",\n "sale",\n "xu_ly",\n "can_bo_sung",\n "ngay_ky_hdmb",\n "ngay_ban_giao",\n {\n "type": "List",\n "entity": "thanh_vien",\n "label": "H\u1ED9 gia \u0111\xECnh",\n "columns": ["ngay_sinh"],\n "create": ["ho_ten", "quan_he", "ngay_sinh", "cccd", "thu_nhap"]\n },\n {\n "type": "List",\n "entity": "giay_to",\n "expect": {"field": "loai", "only": ["can_cuoc", "hon_nhan"]},\n "label": "Gi\u1EA5y t\u1EDD c\xE1 nh\xE2n",\n "columns": ["ngay_nhan"],\n "children": [{"type": "Check", "field": "phai_sua"}]\n },\n {\n "type": "Act",\n "key": "doc_giay_to",\n "label": "\u0110\u1ECDc gi\u1EA5y t\u1EDD",\n "intake": "giay_to",\n "fills": [\n "doi_tuong",\n "tinh_trang_nha_o",\n "giay_chung_nhan_so",\n "dien_tich_nha",\n "khach_hang.ho_ten",\n "khach_hang.cccd",\n "khach_hang.ngay_sinh",\n "khach_hang.dia_chi",\n "thanh_vien"\n ]\n },\n {\n "type": "Act",\n "key": "giao_xu_ly",\n "label": "Giao ng\u01B0\u1EDDi x\u1EED l\xFD",\n "asks": [{"input": "nguoi", "label": "Ng\u01B0\u1EDDi x\u1EED l\xFD", "type": "member", "required": true}],\n "set": {"xu_ly": {"input": "nguoi"}}\n },\n {\n "type": "Act",\n "key": "chuyen_du_an",\n "label": "Chuy\u1EC3n d\u1EF1 \xE1n",\n "asks": [\n {\n "input": "du_an_moi",\n "label": "Sang d\u1EF1 \xE1n",\n "type": "link",\n "entity": "du_an",\n "required": true,\n "add": ["ten_du_an", "chu_dau_tu", "so_can"]\n }\n ],\n "set": {"du_an": {"input": "du_an_moi"}},\n "confirm": true\n }\n ]\n },\n {\n "type": "Section",\n "key": "bo_ho_so",\n "title": "B\u1ED9 h\u1ED3 s\u01A1",\n "at": ["ngay_nhan_giay_to", "ngay_lap_ho_so"],\n "children": [\n "ngay_nhan_giay_to",\n "ngay_lap_ho_so",\n {\n "type": "Act",\n "key": "tao_giay_to",\n "label": "T\u1EA1o gi\u1EA5y t\u1EDD",\n "templates": [\n {"template": "don_dang_ky"},\n {"template": "mau_02_chua_co_nha", "when": {"tinh_trang_nha_o": ["chua_co_nha"]}},\n {"template": "mau_03_nha_chat", "when": {"tinh_trang_nha_o": ["nha_chat"]}},\n {"template": "mau_05_thu_nhap", "when": {"doi_tuong": ["thu_nhap_thap"]}}\n ],\n "into": "bo_ho_so",\n "when": {"giai_doan": ["ngay_nhan_giay_to", "ngay_lap_ho_so"]}\n },\n {\n "type": "Act",\n "key": "nop_cdt",\n "label": "N\u1ED9p ch\u1EE7 \u0111\u1EA7u t\u01B0",\n "when": {"dich_vu": ["tron_goi"], "giai_doan": ["ngay_lap_ho_so"]},\n "requires": ["bo_ho_so", "loai_can"],\n "asks": ["loai_can"],\n "set": {"giai_doan": "ngay_nop_cdt"},\n "confirm": true\n },\n {\n "type": "Act",\n "key": "to_ngay_nhan_giay_to",\n "label": "\u0110\xE3 nh\u1EADn gi\u1EA5y t\u1EDD",\n "when": {"giai_doan": {"empty": true}, "dich_vu": ["tron_goi"]},\n "asks": [{"field": "ngay_nhan_giay_to", "starts": "today"}],\n "set": {"giai_doan": "ngay_nhan_giay_to"}\n },\n {\n "type": "Act",\n "key": "to_ngay_lap_ho_so",\n "label": "\u0110\xE3 l\u1EADp h\u1ED3 s\u01A1",\n "when": {"giai_doan": ["ngay_nhan_giay_to"], "dich_vu": ["tron_goi"]},\n "asks": [{"field": "ngay_lap_ho_so", "starts": "today"}],\n "set": {"giai_doan": "ngay_lap_ho_so"}\n },\n {\n "type": "Act",\n "key": "to_ngay_ban_giao",\n "label": "\u0110\xE3 b\xE0n giao h\u1ED3 s\u01A1",\n "when": {"giai_doan": ["ngay_nhan_giay_to"], "dich_vu": ["lam_ho_so"]},\n "asks": [{"field": "ngay_ban_giao", "starts": "today"}],\n "set": {"giai_doan": "ngay_ban_giao"}\n }\n ]\n }\n ]\n },\n {\n "type": "Act",\n "key": "in_danh_sach",\n "label": "In danh s\xE1ch n\u1ED9p ch\u1EE7 \u0111\u1EA7u t\u01B0",\n "on": "view",\n "template": "danh_sach_nop"\n }\n ]\n }\n ]\n }\n}\n```\n\n## Calls\n\n**The job.** Call a lead from their record, and keep what was said as a touch on its timeline.\n\n**Read.** An Act\'s `record` (into the `timeline` child: audio, transcript, when and who) and `fills` (the touch\'s fields an agent fills from the transcript).\n\n**Keys.** `model/act-node`, `model/layouts`\n\n**Not when.** Calls made and logged elsewhere \u2014 a `timeline` with its own add keeps the note.\n\n```json\n{\n "tables": [\n {\n "ref": "khach_tiem_nang",\n "title": "ho_ten",\n "fields": {\n "ho_ten": {"type": "text"},\n "ten_cong_ty": {"type": "text"},\n "vai_tro": {\n "type": "select",\n "options": [\n {"alias": "chu_doanh_nghiep", "color": "emerald"},\n {"alias": "ke_toan_truong", "color": "blue"},\n {"alias": "quan_ly", "color": "teal"},\n {"alias": "nhan_vien", "color": "sky"},\n {"alias": "chua_ro", "color": "zinc"}\n ]\n },\n "loai_hinh": {\n "type": "select",\n "options": [\n {"alias": "thuong_mai", "color": "blue"},\n {"alias": "san_xuat", "color": "amber"},\n {"alias": "dich_vu", "color": "violet"},\n {"alias": "xay_dung", "color": "orange"},\n {"alias": "ho_kinh_doanh", "color": "teal"},\n {"alias": "chua_ro", "color": "zinc"}\n ]\n },\n "quy_mo": {\n "type": "select",\n "options": [\n {"alias": "q_1_10", "color": "sky"},\n {"alias": "q_11_50", "color": "blue"},\n {"alias": "q_51_200", "color": "indigo"},\n {"alias": "q_200", "color": "violet"},\n {"alias": "chua_ro", "color": "zinc"}\n ]\n },\n "so_dien_thoai": {"type": "text"},\n "zalo": {"type": "text"},\n "email": {"type": "text"},\n "trang_ca_nhan": {"type": "text", "format": "link"},\n "anh": {"type": "files"},\n "trang_thai": {\n "type": "select",\n "options": [\n {"alias": "moi", "color": "gray"},\n {"alias": "du_dieu_kien", "color": "blue"},\n {"alias": "da_lien_he", "color": "amber"},\n {"alias": "da_chuyen_doi", "color": "emerald"},\n {"alias": "loai", "color": "red"}\n ]\n },\n "ly_do_loai": {\n "type": "select",\n "options": [\n {"alias": "khong_phu_hop", "color": "orange"},\n {"alias": "qua_nho", "color": "amber"},\n {"alias": "chua_co_ngan_sach", "color": "yellow"},\n {"alias": "sai_nguoi", "color": "blue"},\n {"alias": "yeu_cau_dung", "color": "red"},\n {"alias": "trung", "color": "zinc"}\n ]\n },\n "vi_sao_du_dieu_kien": {"type": "text"},\n "phu_trach": {"type": "member"},\n "nguon": {"type": "link", "table": "kenh", "multi": false},\n "lien_he": {"type": "link", "table": "lien_he", "multi": false},\n "cong_ty": {"type": "link", "table": "cong_ty", "multi": false},\n "ngay_chuyen_doi": {"type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},\n "ghi_chu": {"type": "text", "format": "markdown"},\n "tin_hieu": {"type": "link", "table": "tin_hieu", "multi": true},\n "buoc_tiep": {"type": "link", "table": "buoc_tiep", "multi": true},\n "tuong_tac": {"type": "link", "table": "tuong_tac", "multi": true},\n "do_nong": {\n "type": "formula",\n "options": [\n {"alias": "nong", "label": "N\xF3ng", "color": "orange"},\n {"alias": "nguoi", "label": "Ngu\u1ED9i", "color": "sky"}\n ]\n },\n "han_buoc_tiep": {"type": "rollup"},\n "khong_lien_he": {"type": "formula"}\n },\n "records": {\n "subtitle": ["ten_cong_ty"],\n "image": "anh",\n "party": "person",\n "due": ["han_buoc_tiep"],\n "status": {\n "field": "trang_thai",\n "closed": ["da_chuyen_doi", "loai"],\n "dates": {"da_chuyen_doi": "ngay_chuyen_doi"}\n }\n }\n },\n {\n "ref": "tin_hieu",\n "title": "tom_tat",\n "fields": {\n "tom_tat": {"type": "text"},\n "noi_dung": {"type": "text", "format": "markdown"},\n "anh_chup": {"type": "files"},\n "lien_ket": {"type": "text", "format": "link"},\n "kenh": {"type": "link", "table": "kenh", "multi": false},\n "loai": {\n "type": "select",\n "options": [\n {"alias": "hoi_dich_vu", "color": "emerald"},\n {"alias": "than_phien", "color": "blue"},\n {"alias": "tuyen_ke_toan", "color": "amber"},\n {"alias": "moi_thanh_lap", "color": "violet"},\n {"alias": "gioi_thieu", "color": "teal"},\n {"alias": "danh_ba", "color": "zinc"},\n {"alias": "nhieu", "color": "stone"}\n ]\n },\n "ngay_dang": {"type": "date", "timezone": "Asia/Ho_Chi_Minh"},\n "ngay_xay_ra": {"type": "formula"}\n },\n "records": {"subtitle": ["kenh", "ngay_xay_ra"], "image": "anh_chup"}\n },\n {\n "ref": "buoc_tiep",\n "title": "vi_sao_luc_nay",\n "fields": {\n "vi_sao_luc_nay": {"type": "text"},\n "viec": {\n "type": "select",\n "options": [\n {"alias": "binh_luan", "color": "blue", "mark": {"kind": "icon", "name": "message-square"}},\n {"alias": "nhan_facebook", "color": "violet", "mark": {"kind": "brand", "name": "facebook"}},\n {"alias": "nhan_zalo", "color": "sky", "mark": {"kind": "brand", "name": "zalo"}},\n {"alias": "goi_dien", "color": "teal", "mark": {"kind": "icon", "name": "phone"}},\n {"alias": "gui_email", "color": "indigo", "mark": {"kind": "icon", "name": "mail"}},\n {"alias": "gui_bang_gia", "color": "amber", "mark": {"kind": "icon", "name": "receipt"}}\n ]\n },\n "ban_nhap": {"type": "text", "format": "markdown"},\n "han": {"type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},\n "tinh_trang": {\n "type": "select",\n "options": [\n {"alias": "cho", "color": "blue"},\n {"alias": "xong", "color": "emerald"},\n {"alias": "bo_qua", "color": "zinc"}\n ]\n }\n },\n "records": {\n "subtitle": ["viec"],\n "mark": "viec",\n "due": ["han"],\n "status": {"field": "tinh_trang", "closed": ["xong", "bo_qua"]}\n }\n },\n {\n "ref": "tuong_tac",\n "title": "tom_tat",\n "fields": {\n "tom_tat": {"type": "text"},\n "thoi_diem": {"type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},\n "qua": {\n "type": "select",\n "options": [\n {"alias": "goi_dien", "color": "teal", "mark": {"kind": "icon", "name": "phone"}},\n {"alias": "zalo", "color": "sky", "mark": {"kind": "brand", "name": "zalo"}},\n {"alias": "facebook", "color": "blue", "mark": {"kind": "brand", "name": "facebook"}},\n {"alias": "email", "color": "indigo", "mark": {"kind": "icon", "name": "mail"}},\n {"alias": "gap_truc_tiep", "color": "emerald", "mark": {"kind": "icon", "name": "users"}},\n {"alias": "khac", "color": "zinc", "mark": {"kind": "icon", "name": "ellipsis"}}\n ]\n },\n "phan_hoi": {\n "type": "select",\n "options": [\n {"alias": "dang_cho", "color": "blue"},\n {"alias": "da_tra_loi", "color": "green"},\n {"alias": "quan_tam", "color": "amber"},\n {"alias": "khong_phu_hop", "color": "red"},\n {"alias": "khong_phan_hoi", "color": "zinc"},\n {"alias": "sai_so", "color": "stone"}\n ]\n },\n "ghi_am": {"type": "files"},\n "nguyen_van": {"type": "text", "format": "markdown"},\n "hen_lai": {"type": "date", "timezone": "Asia/Ho_Chi_Minh"},\n "nguoi_thuc_hien": {"type": "member"}\n },\n "records": {"subtitle": ["qua", "phan_hoi"]}\n }\n ],\n "body": {\n "type": "App",\n "key": "khach_tiem_nang",\n "name": "Kh\xE1ch ti\u1EC1m n\u0103ng",\n "icon": "radar",\n "color": "violet",\n "children": [\n {\n "type": "List",\n "entity": "khach_tiem_nang",\n "columns": ["phu_trach", "han_buoc_tiep"],\n "filters": ["phu_trach"],\n "create": [\n "ho_ten",\n "trang_ca_nhan",\n "so_dien_thoai",\n "ten_cong_ty",\n {"field": "nguon", "add": ["ten", "loai"]}\n ],\n "children": [\n {"type": "Reading", "key": "register_reading_0", "by": "phu_trach"},\n {\n "type": "Reading",\n "key": "register_reading_1",\n "label": "Kh\xE1ch chuy\u1EC3n \u0111\u1ED5i",\n "by": "ngay_chuyen_doi"\n },\n {"type": "Check", "field": "khong_lien_he", "resolve": "da_lien_he"},\n {\n "type": "Record",\n "children": [\n {\n "type": "Section",\n "key": "danh_gia",\n "title": "\u0110\xE1nh gi\xE1",\n "at": ["moi"],\n "children": [\n "vai_tro",\n "loai_hinh",\n "quy_mo",\n "vi_sao_du_dieu_kien",\n "do_nong",\n {\n "type": "List",\n "entity": "tin_hieu",\n "label": "T\xEDn hi\u1EC7u",\n "columns": ["loai", "noi_dung"],\n "create": ["tom_tat", "anh_chup", "loai", "noi_dung", "kenh", "lien_ket", "ngay_dang"]\n },\n {\n "type": "Act",\n "key": "danh_gia_dat",\n "label": "\u0110\u1EE7 \u0111i\u1EC1u ki\u1EC7n",\n "when": {"trang_thai": ["moi"]},\n "requires": ["vi_sao_du_dieu_kien"],\n "set": {"trang_thai": "du_dieu_kien"}\n }\n ]\n },\n {\n "type": "Section",\n "key": "lien_he",\n "title": "Li\xEAn h\u1EC7",\n "at": ["du_dieu_kien"],\n "children": [\n "so_dien_thoai",\n "phu_trach",\n "trang_ca_nhan",\n "zalo",\n "email",\n {"field": "ghi_chu", "block": false},\n {\n "type": "List",\n "entity": "buoc_tiep",\n "label": "B\u01B0\u1EDBc ti\u1EBFp theo",\n "columns": ["ban_nhap"],\n "create": ["vi_sao_luc_nay", "viec", "han", "ban_nhap"],\n "children": [\n {\n "type": "Act",\n "key": "lam_xong",\n "label": "\u0110\xE3 l\xE0m",\n "when": {"tinh_trang": ["cho"]},\n "set": {"tinh_trang": "xong"}\n }\n ]\n },\n {\n "type": "List",\n "entity": "tuong_tac",\n "label": "T\u01B0\u01A1ng t\xE1c",\n "layout": "timeline",\n "create": ["tom_tat", "qua", "phan_hoi", "thoi_diem", "hen_lai"]\n },\n {\n "type": "Act",\n "key": "ghi_cuoc_goi",\n "label": "Ghi \xE2m cu\u1ED9c g\u1ECDi",\n "when": {"trang_thai": ["du_dieu_kien"]},\n "fills": ["tom_tat", "phan_hoi"],\n "record": {\n "into": "tuong_tac",\n "audio": "ghi_am",\n "transcript": "nguyen_van",\n "at": "thoi_diem",\n "by": "nguoi_thuc_hien"\n }\n },\n {\n "type": "Act",\n "key": "da_lien_he",\n "label": "\u0110\xE1nh d\u1EA5u \u0111\xE3 li\xEAn h\u1EC7",\n "when": {"trang_thai": ["du_dieu_kien"]},\n "set": {"trang_thai": "da_lien_he"}\n }\n ]\n },\n {\n "type": "Section",\n "key": "chuyen_doi",\n "title": "Chuy\u1EC3n \u0111\u1ED5i",\n "at": ["da_lien_he"],\n "children": [\n "nguon",\n {"field": "lien_he", "edit": false},\n {"field": "cong_ty", "edit": false},\n {\n "type": "Act",\n "key": "chuyen_doi",\n "label": "Chuy\u1EC3n \u0111\u1ED5i",\n "when": {"trang_thai": ["da_lien_he"]},\n "workflow": "chuyen_doi_khach",\n "confirm": true\n }\n ]\n },\n {\n "type": "Section",\n "key": "loai",\n "title": "Lo\u1EA1i",\n "at": ["loai"],\n "children": [\n "ly_do_loai",\n {\n "type": "Act",\n "key": "loai",\n "label": "Lo\u1EA1i",\n "when": {"trang_thai": ["moi", "du_dieu_kien", "da_lien_he"]},\n "asks": ["ly_do_loai"],\n "set": {"trang_thai": "loai"},\n "danger": true\n }\n ]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Approval\n\n**The job.** Raise a payment request, see it through approval, and read what is waiting at the top of the list.\n\n**Read.** `records.de_nghi.status` with its `history` (each move: when and who), Sections staged by `at`, and the List\'s Readings leading with the headline number. The history stays because the business reads who signed each move; where only the day a stage was reached matters, the status stamps a date on the row instead (the Gantt above).\n\n**Keys.** `model/status`, `model/section-node`, `model/reading-node`\n\n**Not when.** Rows that never move \u2014 a reference list (a price list, a catalog) has no status and no headline number.\n\n```json\n{\n "tables": [\n {\n "ref": "de_nghi",\n "title": "noi_dung",\n "fields": {\n "noi_dung": {"type": "text"},\n "so_de_nghi": {"type": "text"},\n "loai": {\n "type": "select",\n "options": [\n {"alias": "thanh_toan_ncc", "color": "violet"},\n {"alias": "nhan_cong", "color": "indigo"},\n {"alias": "chi_phi_cong_truong", "color": "sky"},\n {"alias": "tam_ung", "color": "amber"},\n {"alias": "hoan_ung", "color": "teal"}\n ]\n },\n "du_an": {"type": "link", "table": "du_an", "multi": false},\n "nha_cung_cap": {"type": "link", "table": "nha_cung_cap", "multi": false},\n "so_tien": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},\n "han_thanh_toan": {"type": "date"},\n "trang_thai": {\n "type": "select",\n "options": [\n {"alias": "nhap", "color": "slate"},\n {"alias": "cho_duyet", "color": "amber"},\n {"alias": "cho_chu_tich", "color": "violet"},\n {"alias": "da_duyet", "color": "blue"},\n {"alias": "da_chi", "color": "green"},\n {"alias": "tu_choi", "color": "rose"}\n ]\n },\n "nguoi_de_nghi": {"type": "member"},\n "chung_tu": {"type": "files"},\n "nguoi_duyet": {"type": "member"},\n "duyet_luc": {"type": "date", "format": "datetime"},\n "ly_do_tu_choi": {"type": "text"},\n "phieu_chi": {"type": "files"},\n "ngan_sach_du_an": {"type": "lookup"},\n "da_duyet_du_an": {"type": "lookup"},\n "vuot_ngan_sach": {"type": "formula"},\n "sat_ngan_sach": {"type": "formula"},\n "da_gui": {"type": "formula"},\n "nhap_at": {"type": "date", "format": "datetime"},\n "cho_duyet_at": {"type": "date", "format": "datetime"},\n "cho_chu_tich_at": {"type": "date", "format": "datetime"},\n "da_duyet_at": {"type": "date", "format": "datetime"}\n },\n "records": {\n "subtitle": ["du_an"],\n "figure": "so_tien",\n "limits": {"da_duyet_du_an": "ngan_sach_du_an"},\n "due": ["han_thanh_toan"],\n "starts": {"nguoi_de_nghi": "me"},\n "status": {\n "field": "trang_thai",\n "closed": ["da_chi", "tu_choi"],\n "dropped": ["tu_choi"],\n "dates": {\n "nhap": "nhap_at",\n "cho_duyet": "cho_duyet_at",\n "cho_chu_tich": "cho_chu_tich_at",\n "da_duyet": "da_duyet_at"\n },\n "history": "lich_su_duyet",\n "stages": {\n "nhap": {"owner": "nguoi_de_nghi"},\n "cho_duyet": {"owner": {"group": "<id of the giam_doc group>"}, "within": 1},\n "cho_chu_tich": {\n "owner": {"group": "<id of the chu_tich group>"},\n "within": 2,\n "when": {"so_tien": {"gt": 200000000}}\n },\n "da_duyet": {"owner": {"group": "<id of the ke_toan group>"}, "within": 2}\n }\n }\n }\n }\n ],\n "body": {\n "type": "App",\n "key": "de_nghi_chi",\n "name": "\u0110\u1EC1 ngh\u1ECB thanh to\xE1n",\n "icon": "hand-coins",\n "color": "amber",\n "children": [\n {\n "type": "List",\n "entity": "de_nghi",\n "row": {"subtitle": [{"field": "du_an", "add": ["ten", "ma_du_an", "chu_dau_tu", "ngan_sach"]}]},\n "columns": ["nguoi_de_nghi"],\n "filters": ["nguoi_de_nghi", "du_an"],\n "group": "du_an",\n "create": [\n "noi_dung",\n "loai",\n "du_an",\n {"field": "nha_cung_cap", "add": true},\n "so_tien",\n "han_thanh_toan",\n "nguoi_de_nghi"\n ],\n "children": [\n {\n "type": "Reading",\n "key": "register_reading_0",\n "label": "\u0110\xE3 duy\u1EC7t, ch\u1EDD chi",\n "value": "so_tien",\n "where": {"trang_thai": ["da_duyet"]}\n },\n {"type": "Reading", "key": "register_reading_1", "value": "so_tien", "by": "han_thanh_toan"},\n {"type": "Check", "field": "da_gui", "on": ["update"]},\n {"type": "Check", "field": "vuot_ngan_sach"},\n {"type": "Check", "field": "sat_ngan_sach"},\n {\n "type": "Record",\n "door": "drawer",\n "children": [\n {\n "type": "Section",\n "key": "de_nghi",\n "title": "\u0110\u1EC1 ngh\u1ECB",\n "at": ["nhap"],\n "children": [\n "so_de_nghi",\n "loai",\n "nha_cung_cap",\n "han_thanh_toan",\n "nguoi_de_nghi",\n {"field": "chung_tu", "label": "Ch\u1EE9ng t\u1EEB k\xE8m"},\n {\n "type": "Act",\n "key": "gui_duyet",\n "label": "G\u1EEDi duy\u1EC7t",\n "when": {"trang_thai": ["nhap"]},\n "requires": ["chung_tu", "nguoi_de_nghi"],\n "set": {"trang_thai": "cho_duyet"}\n }\n ]\n },\n {\n "type": "Section",\n "key": "duyet",\n "title": "Duy\u1EC7t",\n "at": ["cho_duyet", "cho_chu_tich"],\n "children": [\n {"field": "nguoi_duyet", "edit": false},\n {"field": "duyet_luc", "edit": false},\n {"field": "ly_do_tu_choi", "edit": false},\n {\n "type": "Act",\n "key": "rut_lai",\n "label": "R\xFAt l\u1EA1i",\n "when": {"trang_thai": ["cho_duyet", "cho_chu_tich"]},\n "set": {"trang_thai": "nhap"},\n "confirm": true\n }\n ]\n },\n {\n "type": "Section",\n "key": "da_chi",\n "title": "\u0110\xE3 chi",\n "at": ["da_chi"],\n "children": [{"field": "phieu_chi", "label": "Phi\u1EBFu chi", "edit": false}]\n }\n ]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Dashboard\n\n**The job.** The owner\'s question \u2014 what came in, what went out, where it went, and what is still unmatched \u2014 answered over one period.\n\n**Read.** An App of Readings and no List: Readings with `over` (the period windows them), a Reading `by` the day, Readings `by` a field, and a List of rows to act on that opens in its `app`.\n\n**Keys.** `model/app-node`, `model/reading-node`\n\n**Not when.** One job\'s headline numbers \u2014 those lead that job\'s List as its Readings.\n\n```json\n{\n "tables": [\n {\n "ref": "giao_dich",\n "title": "noi_dung",\n "fields": {\n "noi_dung": {"type": "text"},\n "ngay": {"type": "date"},\n "loai": {"type": "select", "options": [{"alias": "thu", "color": "green"}, {"alias": "chi", "color": "rose"}]},\n "so_tien": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},\n "bien_dong": {"type": "formula", "notation": {"style": "currency", "currency": "VND"}},\n "doi_tuong": {"type": "link", "table": "doi_tuong", "multi": false},\n "danh_muc": {\n "type": "select",\n "options": [\n {"alias": "doanh_thu", "color": "green"},\n {"alias": "hang_hoa", "color": "blue"},\n {"alias": "van_chuyen", "color": "indigo"},\n {"alias": "nhan_su", "color": "violet"},\n {"alias": "van_phong", "color": "sky"},\n {"alias": "thue_phi", "color": "slate"},\n {"alias": "ngan_hang", "color": "teal"},\n {"alias": "khac", "color": "gray"}\n ]\n },\n "sao_ke": {"type": "link", "table": "sao_ke", "multi": false},\n "hoa_don": {"type": "link", "table": "hoa_don", "multi": true},\n "trang_thai": {\n "type": "select",\n "options": [\n {"alias": "chua_khop", "color": "amber"},\n {"alias": "da_khop", "color": "green"},\n {"alias": "khong_can_hd", "color": "slate"}\n ]\n },\n "khop_luc": {"type": "date", "format": "datetime"}\n },\n "records": {\n "subtitle": ["ngay"],\n "figure": "bien_dong",\n "status": {"field": "trang_thai", "closed": ["da_khop", "khong_can_hd"], "dates": {"da_khop": "khop_luc"}}\n }\n },\n {\n "ref": "hoa_don",\n "title": "doi_tuong",\n "fields": {\n "doi_tuong": {"type": "link", "table": "doi_tuong", "multi": false},\n "so_hoa_don": {"type": "text"},\n "ngay_hoa_don": {"type": "date"},\n "so_ngay_qua_han": {"type": "formula", "notation": {"style": "unit", "unit": "day"}},\n "chieu": {\n "type": "select",\n "options": [{"alias": "ban_ra", "color": "green"}, {"alias": "mua_vao", "color": "violet"}]\n },\n "tong_cong": {"type": "formula", "notation": {"style": "currency", "currency": "VND"}},\n "thue_suat": {"type": "formula", "notation": {"style": "unit", "unit": "percent"}},\n "tep": {"type": "files"},\n "giao_dich": {"type": "link", "table": "giao_dich", "multi": true}\n },\n "records": {"subtitle": ["so_hoa_don", "ngay_hoa_don"], "image": "tep", "figure": "tong_cong"}\n }\n ],\n "body": {\n "type": "App",\n "key": "dong_tien",\n "name": "D\xF2ng ti\u1EC1n",\n "icon": "chart-line",\n "color": "emerald",\n "children": [\n {\n "type": "Reading",\n "key": "dashboard_reading_0",\n "label": "Ti\u1EC1n v\xE0o",\n "entity": "giao_dich",\n "value": "so_tien",\n "where": {"loai": ["thu"]},\n "over": "ngay"\n },\n {\n "type": "Reading",\n "key": "dashboard_reading_1",\n "label": "Ti\u1EC1n ra",\n "entity": "giao_dich",\n "value": "so_tien",\n "where": {"loai": ["chi"]},\n "over": "ngay",\n "better": "down"\n },\n {\n "type": "Reading",\n "key": "dashboard_reading_2",\n "label": "D\xF2ng ti\u1EC1n r\xF2ng",\n "entity": "giao_dich",\n "value": "bien_dong",\n "by": "ngay"\n },\n {\n "type": "Reading",\n "key": "dashboard_reading_3",\n "label": "Chi theo danh m\u1EE5c",\n "entity": "giao_dich",\n "value": "so_tien",\n "where": {"loai": ["chi"]},\n "by": "danh_muc",\n "over": "ngay"\n },\n {\n "type": "Reading",\n "key": "dashboard_reading_4",\n "label": "Thu theo kh\xE1ch h\xE0ng",\n "entity": "giao_dich",\n "value": "so_tien",\n "where": {"loai": ["thu"]},\n "by": "doi_tuong",\n "over": "ngay"\n },\n {\n "type": "Reading",\n "key": "dashboard_reading_5",\n "label": "C\xF4ng n\u1EE3 b\xE1n ra theo tu\u1ED5i n\u1EE3",\n "entity": "hoa_don",\n "value": "tong_cong",\n "where": {"chieu": ["ban_ra"]},\n "by": "doi_tuong",\n "across": {"field": "so_ngay_qua_han", "tiers": [1, 8, 16, 31]}\n },\n {\n "type": "Reading",\n "key": "dashboard_reading_6",\n "label": "S\u1ED1 ng\xE0y qu\xE1 h\u1EA1n b\xECnh qu\xE2n",\n "entity": "hoa_don",\n "value": {"avg": "so_ngay_qua_han"},\n "where": {"chieu": ["ban_ra"]}\n },\n {\n "type": "Reading",\n "key": "dashboard_reading_7",\n "label": "Thu\u1EBF su\u1EA5t b\xECnh qu\xE2n",\n "entity": "hoa_don",\n "value": {"avg": "thue_suat"}\n },\n {\n "type": "List",\n "key": "dashboard_reading_8",\n "entity": "giao_dich",\n "where": {"trang_thai": ["chua_khop"]},\n "app": "doi_chieu",\n "label": "D\xF2ng ch\u01B0a kh\u1EDBp",\n "columns": ["sao_ke"]\n }\n ]\n }\n}\n```\n';
|
|
@@ -27098,7 +27098,8 @@ var MODEL_RULES = {
|
|
|
27098
27098
|
},
|
|
27099
27099
|
"app.rates-in-passing": {
|
|
27100
27100
|
sentence: "An App with no Act, log or Tabs of its own whose rows another App's lines copy a number off (`default_from` through a link) \u2014 the rates a quote is priced from \u2014 is worked only while that record is written: a child List, a Section or an Act of that App, never an App of its own.",
|
|
27101
|
-
note: true
|
|
27101
|
+
note: true,
|
|
27102
|
+
stored: true
|
|
27102
27103
|
},
|
|
27103
27104
|
"app.row": { sentence: "A List's `row` states only what differs from `records`: a `subtitle` that is not the title, a number for its `figure`, dates for its `due`, an `image` that is a files picture no Act makes documents into, or `false` for a figure, deadlines or picture the entity states and the App draws none of." },
|
|
27104
27105
|
"app.tasks": {
|
|
@@ -27118,6 +27119,16 @@ var MODEL_RULES = {
|
|
|
27118
27119
|
sentence: "A value a Workflow takes, and a param a Query takes, that states no `description`: the one line a caller reads to fill it.",
|
|
27119
27120
|
note: true
|
|
27120
27121
|
},
|
|
27122
|
+
"app.keys-unique": { sentence: "No two children of one node share a key: an address names one node." },
|
|
27123
|
+
"app.states-list": { sentence: "A register App states the List whose rows it lists; an App stating none is a dashboard and holds Readings." },
|
|
27124
|
+
"app.period": { sentence: "An App's `period` is a dashboard's: a register states its own on its List." },
|
|
27125
|
+
"app.dashboard-words": { sentence: "A dashboard reads every table's rows whole and states no `words` or `scope`: those are a register App's." },
|
|
27126
|
+
"app.add-once": { sentence: "A table's add by a picker is stated once \u2014 at one Field's `add` or one link input's `add` \u2014 and every picker of that table adds by it." },
|
|
27127
|
+
"app.card-once": { sentence: "A table's card (`record`, `remove` on a link Field) is stated once, at one Field." },
|
|
27128
|
+
"app.card-fields": { sentence: "A card's `record` names, once each, fields of the linked table its save may change." },
|
|
27129
|
+
"app.picker-link": { sentence: "`add`, `record` and `remove` stand on a Field that is a link: only a link has a picker and a card." },
|
|
27130
|
+
"app.add-filed": { sentence: "The Lists adding rows of one table file them by one link, the first such List's: a List over that table filed by another link lists its rows and adds none." },
|
|
27131
|
+
"app.default-breaks": { sentence: "A node the platform draws where the tree states none leaves the App refusing nothing more: where it would, the tree states that node itself." },
|
|
27121
27132
|
"app.declines": {
|
|
27122
27133
|
sentence: "A stored body's `declines` names decided rules that fire on the app, each with why it stays as it is: by id, and `records.picture:<entity>`, said once per entity, with what it fires on.",
|
|
27123
27134
|
stored: true
|
|
@@ -27203,6 +27214,21 @@ var MODEL_RULES = {
|
|
|
27203
27214
|
note: true,
|
|
27204
27215
|
design: "rows-in-time"
|
|
27205
27216
|
},
|
|
27217
|
+
"register.where-bound": { sentence: 'A bound read from the record (`{ "record": \u2026 }`) in `where` narrows only a List that reads its rows \u2014 `same`, `app`, or a Record with `edit: false`.' },
|
|
27218
|
+
"register.where-reader": { sentence: 'A List\'s `where` keeps its rows for every reader and never names the reader (`{ "me": true }`): `opens` opens a List on the reader.' },
|
|
27219
|
+
"register.props": {
|
|
27220
|
+
sentence: "A node states only the props of its place: a List those of the register, a log, a roster's cells, a rows, agenda, timeline or read-only List of a record, a document or a dashboard; the Record of a List below the register `edit` and its children; a dashboard's Tab its `key`, `label` and children."
|
|
27221
|
+
},
|
|
27222
|
+
"register.holds": {
|
|
27223
|
+
sentence: "A List holds what its place draws: the register its Tabs, Readings, Progress, Exports, Acts, Checks, Books, Record and, under a roster, the one List of its cells; a child List its Acts, Checks, Record and, with `under`, the List of its documents; a document and a roster's cells their Acts, Checks and Record."
|
|
27224
|
+
},
|
|
27225
|
+
"register.layout-place": {
|
|
27226
|
+
sentence: "The register lays out as a table, cards, a calendar, a roster, a gantt, lanes or a plan; a child List writing its rows as a table, a checklist or a desk; any child List as an agenda or a timeline."
|
|
27227
|
+
},
|
|
27228
|
+
"register.row-restates": {
|
|
27229
|
+
sentence: "A child List and a log read their rows as `records` states them: their `row` states a picker's add or a card on the title or a subtitle, never another `subtitle`, a `figure`, `due`, `image` or `status: false`."
|
|
27230
|
+
},
|
|
27231
|
+
"register.edit-column": { sentence: 'An edit column (`{ "field": \u2026, "edit": true }`) stands in a child List, which writes its rows in place: the register\'s rows are written where they open.' },
|
|
27206
27232
|
// ─── Readings ────────────────────────────────────────────────────────────────
|
|
27207
27233
|
"reading.value": { sentence: "A Reading sums a number, never a percent \u2014 percents do not add up; a value of `{ avg }` reads one." },
|
|
27208
27234
|
"reading.average": {
|
|
@@ -27227,6 +27253,12 @@ var MODEL_RULES = {
|
|
|
27227
27253
|
},
|
|
27228
27254
|
"reading.restate": { sentence: "A dashboard's count tile never counts the rows a List beside it lists under the same `where` and tab: the List's head counts them." },
|
|
27229
27255
|
"reading.period": { sentence: "A dashboard's `period` is the one it opens on, and it stands only over a Reading placed in time by `over`." },
|
|
27256
|
+
"reading.funnel-place": { sentence: "A Reading with `steps` (a funnel) stands in a part of a Record: a band and a dashboard draw none." },
|
|
27257
|
+
"reading.on-hand-split": { sentence: "A Reading with `on_hand` compares what is owed with what is on hand per value of one field, or two: its `by` and `across` name fields, never bands." },
|
|
27258
|
+
"reading.average-single": { sentence: "An average (`value: { avg }`) is a single figure's: its Reading states no `on_hand`, `by` or `across`." },
|
|
27259
|
+
"reading.grid-rows": { sentence: "`across` splits a grid's columns, and stands only beside the `by` splitting its rows." },
|
|
27260
|
+
"reading.tab-holds": { sentence: "A register's Tab narrows its rows and holds no nodes: Readings stand beside it in the List." },
|
|
27261
|
+
"reading.band-off": { sentence: "`readings: false` turns off the band a register leads with by default: a List stating it states no Readings or Progress, which are the band." },
|
|
27230
27262
|
// ─── The record ──────────────────────────────────────────────────────────────
|
|
27231
27263
|
"record.placed-once": {
|
|
27232
27264
|
sentence: "A Field, a child List, a Reading, a Statement and an Act each stand once in a Record: its head draws the title, subtitle, image, status, figure and folder, and a field's one other place is a part's Fields, a gallery or prose Field (`block`) or a Statement \u2014 save the image, which one files gallery may draw again, whole, the figure, which a Statement draws as what it comes to, and rows one List prices and another compares (`compare`); two places whose `when`s never hold together (a select at options the other's lacks) each draw it, and a plain child List (no `expect`, `under`, `pick`, `compare`, nor a `layout` of desk or checklist) likewise."
|
|
@@ -27238,7 +27270,7 @@ var MODEL_RULES = {
|
|
|
27238
27270
|
sentence: "A Field of a part never looks up what its link already reads where it stands: the linked row's title beside the link, the figure or bound its meter reads on the head's line."
|
|
27239
27271
|
},
|
|
27240
27272
|
"record.section-holds": { sentence: "A part (Section, Side, Page) holds Fields, child Lists, Readings, Progress, Statements or Acts." },
|
|
27241
|
-
"record.section-reads": { sentence: "A Field a part shows as a value only (`edit: false`) is one that part draws." },
|
|
27273
|
+
"record.section-reads": { sentence: "A Field a part shows as a value only (`edit: false`) is one that part draws.", stored: true },
|
|
27242
27274
|
"record.compose": {
|
|
27243
27275
|
sentence: "A Record's `compose` is stated instead of parts (it holds no children), on a page `door`, by an App with no Books whose records have no owned stages (`status.stages`) and no picture (`image`) \u2014 a composed page draws neither. Each name is a field of the entity, an Act of the app on the record or a child entity linked to it; one `Title`, and each field, Act and child drawn once \u2014 a field by `Field` or `Document`, never the title or the status, which `Title` and `Status` draw. The first `Act` the page itself draws is its next step and every other one secondary; an `Act` is a button alone, never one making a set of papers. A `Bar` holds no `Rows` and no `Document`, and an `Act` in it that destroys or corrects what is done (`danger`, or a move back) stands in the record's \u22EF instead (the Bar piece's own row says how it draws). A `Panel` draws nothing in the page: it is one entry of the record's \u22EF, named by its `title`, opening its pieces in a panel beside the page; it stands directly in the root `Column`, holds `Rows`, `Field`, `Act`, `Group` and `Document` \u2014 never `Title`, `Status`, `Bar`, `Copy` or another `Panel` \u2014 and an `Act` directly in it ends the panel's name, secondary. A `Group` is headed once, by its `title`: holding one `Rows`, that heading carries the rows' count and their add in place of the rows' own, and the `Act`s the group holds end its line. A `Copy` takes a text field's words as the page holds them now, a `Document`'s words not yet saved among them; a field a `Copy` alone names is read, never written."
|
|
27244
27276
|
},
|
|
@@ -27302,6 +27334,10 @@ var MODEL_RULES = {
|
|
|
27302
27334
|
"record.field-edit": {
|
|
27303
27335
|
sentence: "An Act that only writes one field of its record, not the status, from the one input it asks is that field's edit: the Field rests in the part as a control."
|
|
27304
27336
|
},
|
|
27337
|
+
"record.holds": {
|
|
27338
|
+
sentence: "A Record holds parts (Section, Side, Page) or the Fields of its one part, never both; the Record of a Book, or of a child List no row opens in place (an agenda, a timeline, a read-only List), holds Fields alone."
|
|
27339
|
+
},
|
|
27340
|
+
"record.field-heading": { sentence: "A Field drawn as a control states no `label` or `when`: only a gallery or prose Field (`block`) has a heading and a condition." },
|
|
27305
27341
|
// ─── Blocks ──────────────────────────────────────────────────────────────────
|
|
27306
27342
|
"block.child-link": { sentence: "A child List reads a child linked to the record; `via` names the link where the child links more than once." },
|
|
27307
27343
|
"block.row-blocks": {
|
|
@@ -27328,7 +27364,8 @@ var MODEL_RULES = {
|
|
|
27328
27364
|
sentence: "A child List's editable columns (`{ \"field\", \"edit\": true }`) name at most two of the child's columns or its figure that the App's saves of the child write \u2014 a number, a date, a few words, one option or a one-row link, never a files or long-text field, the List's `via` nor a link it must name alike (a link's `same`) \u2014 never on an `expect` List."
|
|
27329
27365
|
},
|
|
27330
27366
|
"register.remove": {
|
|
27331
|
-
sentence: "A List's Delete stands only where the App writes its rows: `remove: true` over rows it cannot write is refused. Absent, rows offer a Delete wherever the List writes them, and `remove: false` takes it away."
|
|
27367
|
+
sentence: "A List's Delete stands only where the App writes its rows: `remove: true` over rows it cannot write is refused. Absent, rows offer a Delete wherever the List writes them, and `remove: false` takes it away.",
|
|
27368
|
+
stored: true
|
|
27332
27369
|
},
|
|
27333
27370
|
"register.remove-roster": {
|
|
27334
27371
|
sentence: "A roster cells List's `remove: true` offers the Delete of the roster's rows, so it stands only beside a roster over rows the App writes."
|
|
@@ -27337,7 +27374,8 @@ var MODEL_RULES = {
|
|
|
27337
27374
|
sentence: "`attention: false` keeps a table's or cards' rows in the List's own order \u2014 a first-come queue; a calendar, lanes, a gantt and a roster stand their rows in time and lift none."
|
|
27338
27375
|
},
|
|
27339
27376
|
"block.remove": {
|
|
27340
|
-
sentence: "A child List's `remove: true` stands only where the App writes those rows."
|
|
27377
|
+
sentence: "A child List's `remove: true` stands only where the App writes those rows.",
|
|
27378
|
+
stored: true
|
|
27341
27379
|
},
|
|
27342
27380
|
"block.remove-documents": {
|
|
27343
27381
|
sentence: "The documents List inside a child List with `under` states `remove: true` only over documents the App writes."
|
|
@@ -27384,7 +27422,11 @@ var MODEL_RULES = {
|
|
|
27384
27422
|
},
|
|
27385
27423
|
"block.agenda": { sentence: "An agenda List's child names a date on its row line (`records` title or subtitle); the layout's `start` is a date of the record." },
|
|
27386
27424
|
"block.timeline": { sentence: "A timeline List's child holds a date to place its entries by." },
|
|
27387
|
-
"block.
|
|
27425
|
+
"block.create-boolean": { sentence: "A document's and a roster's cells' `create` is `true` or `false`: their add asks the table's defaults." },
|
|
27426
|
+
"block.via-path": { sentence: "A `<link>.<link>` `via` reaches rows through a row of their own and only reads them: a List writing its rows lists a child linked to the row it stands under." },
|
|
27427
|
+
"block.read-label": { sentence: "A List that only reads its rows \u2014 a read-only child List, an Outside's List, a dashboard's List \u2014 states its `label`." },
|
|
27428
|
+
"block.create-empty": { sentence: "A child List that adds rows asks the fields of its add: where the table's defaults ask none, state `create: false`, or the fields it asks." },
|
|
27429
|
+
"block.field-kind": { sentence: "A gallery Field names a files field of the record, and a prose Field a text field.", stored: true },
|
|
27388
27430
|
"statement.lines": {
|
|
27389
27431
|
sentence: "A Statement's `lines` are numbers of the record, each once \u2014 the first stating no `op`, every other one, the last an `equals` \u2014 each `equals` a formula, rollup or lookup, and a `rate` a number of the record."
|
|
27390
27432
|
},
|
|
@@ -27433,14 +27475,15 @@ var MODEL_RULES = {
|
|
|
27433
27475
|
design: "moves"
|
|
27434
27476
|
},
|
|
27435
27477
|
"app.adds-entity": { sentence: "A stored body's `adds` name an entity the app reaches.", stored: true },
|
|
27436
|
-
"app.adds-field": { sentence: "A
|
|
27437
|
-
"app.picks-only-entity": { sentence: "A Field or a row title that states `add: false` stands in an entity the app reaches." },
|
|
27478
|
+
"app.adds-field": { sentence: "A picker's `add` names fields of its table a person writes, each once." },
|
|
27479
|
+
"app.picks-only-entity": { sentence: "A Field or a row title that states `add: false` stands in an entity the app reaches.", stored: true },
|
|
27438
27480
|
"app.picks-only-field": { sentence: "`add: false` stands at a link, once, to an entity whose add the app states." },
|
|
27439
|
-
"app.cards-entity": { sentence: "A Field or a row title that states `record` or `remove` stands at a link to an entity the app reaches." },
|
|
27481
|
+
"app.cards-entity": { sentence: "A Field or a row title that states `record` or `remove` stands at a link to an entity the app reaches.", stored: true },
|
|
27440
27482
|
"app.remove": {
|
|
27441
27483
|
sentence: "An App that writes its register's records offers a Delete of them wherever the List writes them, until the List says `remove: false`.",
|
|
27442
27484
|
decide: true,
|
|
27443
|
-
design: "sections"
|
|
27485
|
+
design: "sections",
|
|
27486
|
+
stored: true
|
|
27444
27487
|
},
|
|
27445
27488
|
"app.edits": {
|
|
27446
27489
|
sentence: "A stored body states the fields its saves may change (`edits`, by entity): the fields its surfaces write less what its own Acts change \u2014 a record's save, a child row's, an in-place edit, a lane, gantt or plan move; a body stating none saves nothing.",
|
|
@@ -27518,6 +27561,7 @@ var MODEL_RULES = {
|
|
|
27518
27561
|
"act.review": {
|
|
27519
27562
|
sentence: "`review: true` stands beside an authored `workflow` on an Act over the record, the ticked rows or every row in view, in an App over one entity \u2014 never in an App with Books, nor beside `set`, `template`, `templates`, `intake`, `record`, `sign` or `confirm`, nor in a child List, and it asks inputs only, none named `approved`. Its workflow is that Act's alone, run by no other Act. The workflow calls only steps that read \u2014 no agent \u2014 and returns a changeset of the entity's table in `data.review`, the same for the same inputs and rows; the platform writes the rows `approved` names, each only while the run proposes it as the caller read it. Over every row in view (`on: \"view\"`) it is handed no rows and states no `when`: its workflow reads the rows it may change, whatever the view is narrowed to, and no bound on a run's rows applies."
|
|
27520
27563
|
},
|
|
27564
|
+
"act.set-tagged": { sentence: 'An Act\'s `set` states the moment as `{ "now": true }`, the person pressing as `{ "me": true }` and an input as `{ "input": \u2026 }` \u2014 never the strings `"now"`, `"me"` or `"input:\u2026"`.' },
|
|
27521
27565
|
// ─── Checks ──────────────────────────────────────────────────────────────────
|
|
27522
27566
|
"check.field": { sentence: "A Check's `field` is a yes/no or text formula of the rows of the List it stands in." },
|
|
27523
27567
|
"check.of": { sentence: "A Check stands in the register List or in a child List the Record lists." },
|