@lotics/cli 0.316.3 → 0.316.4
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 +3 -3
- package/docs/first_build.md +1 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -1729,8 +1729,8 @@ async function saveSignedLinks(links, dir) {
|
|
|
1729
1729
|
}
|
|
1730
1730
|
|
|
1731
1731
|
// src/version.ts
|
|
1732
|
-
var VERSION = "0.316.
|
|
1733
|
-
var APP_SDK_VERSION = "0.124.
|
|
1732
|
+
var VERSION = "0.316.4";
|
|
1733
|
+
var APP_SDK_VERSION = "0.124.2";
|
|
1734
1734
|
|
|
1735
1735
|
// src/timezone.ts
|
|
1736
1736
|
function machineTimezone() {
|
|
@@ -1749,7 +1749,7 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
1749
1749
|
var getting_started_default = '# Getting started\n\nA workspace is where one business keeps what it knows and does its daily work. **Records** are what it tracks, one table per kind of thing and one row per entry. **Apps** are the screens a team works in, one per person\'s job, drawn over the tables. **Documents** are the quotes, invoices and slips it sends, each a template filled from a record, or from the many rows of a period or a statement. A paper is the workspace\'s once it is made from a template or uploaded and filed on its record; a file left on the host\'s own disk, the user\'s included, reaches no one in the workspace. A procedure or policy no table holds is a **knowledge doc**; a price list, a customer list or the open orders are rows.\n\nOpen the one page for the job. Pages are read with the `docs` tool (`docs path="design"`; `lotics docs design` on the CLI).\n\n- **Build a workspace from a description:** `docs path="first_build"`. Ask the owner once, only what their papers and your defaults cannot settle, usually their papers and lists first, then a whole bakery built call by call: tables with a check, the paper, the app (which apps, a treatment per kind of row, what the check refuses, who gets it), the owner\'s rows, then pressing it.\n- **Change an app:** `docs path="model/changing-an-app"`. `get_app`, then `set_app` with `nodes` and `base_version_id`. A table is changed with `get_table`, then `set_tables` with its `table_id` (`data_model`).\n- **Enforce a rule on a table:** `docs path="workflows/table-checks"`. A condition no create, update or delete may meet, whoever makes it, and the language it is written in.\n- **Make a document:** `docs path="document_templates"`. A template, its slots and loops, and the act that files it on a record.\n- **Enter data, count, answer a question:** `docs path="working_in_a_workspace"`. The right tool for each job; the value each field takes is `field_values`, choosing rows is `filters`.\n- **Reach a connected service, or do what a button cannot:** `docs path="workflows"`. Triggers, steps and expressions; each service step is in `workflow_steps`.\n- **Share an app:** `docs path="first_build"`, \xA7 3. `set_access`, and which app each person gets.\n\nWhich apps a business needs is `first_build`, \xA7 3 (a worked split: `app_architecture`); how each record is drawn is `design`; every key is in `model`; each screen in a complete workspace is `examples`.\n';
|
|
1750
1750
|
|
|
1751
1751
|
// docs/first_build.md
|
|
1752
|
-
var first_build_default = '# First build \u2014 a whole workspace, call by call\n\nA bakery takes custom-cake orders at the counter. An order is a customer and the cakes on it, one line per cake from the menu; it moves taken, baking, ready, collected, and a slip of the cakes prints when it is ready. Six steps, in the order every workspace is built.\n\nStep 0, before the first write: ask the owner once, in one message, only what you cannot settle yourself. Usually: their papers and lists \u2014 a photo, a spreadsheet or a file is fine; they answer most questions (what they send and receive, customers, items, prices) \u2014 and what no paper tells you that would change the tables or who does which step. The owner answers by hand, so every question costs them time: ask nothing they gave already or a paper settles. Each question is one sentence, no sub-questions or bullets under it; nothing else in that message. Wait for the answer, then build; never build first and ask after. Never ask about stages or design: take a default for everything else and list it in your report so the owner can correct it. Load the owner\'s own rows. `get_workspace` shows what already exists; what no one could guess from the data goes into a knowledge doc with `set_knowledge` (`knowledge_docs`).\n\n## 1. Tables and their checks: `set_tables`\n\nOne call, linked tables together; a call names a table by its alias, a new table in the call by its `ref`, which becomes its alias; a link names the other table so. Three kinds appear: a book lines are chosen from (`menu_item`), the record the work moves (`cake_order`) and its lines (`cake_line`). One table per kind of thing, one column per fact, a link wherever one record points at another. A status is the stage of the work, six options or fewer; a touch ("called") is a log row, a money fact ("paid in full") a formula, a quantity a number with its unit in `notation`. Every table people work states `singular` (an app with an Add over a table lacking it is refused), `title` (who or what a row is; a code only where nothing else names it) and `records` (an app over a table without it is refused).\n\n```json\n{\n "tables": [\n {\n "ref": "customer", "label": "Customers", "singular": "Customer", "title": "name",\n "fields": {"name": {"type": "text", "label": "Name", "required": true}},\n "records": {"party": "person"}\n },\n {\n "ref": "menu_item", "label": "Menu", "singular": "Menu item", "title": "name",\n "fields": {\n "name": {"type": "text", "label": "Cake", "required": true},\n "photo": {"type": "files", "label": "Photo"},\n "price": {"type": "number", "label": "Price", "required": true, "notation": {"style": "currency", "currency": "USD"}}\n },\n "records": {"figure": "price", "image": "photo"}\n },\n {\n "ref": "cake_order", "label": "Cake orders", "singular": "Cake order", "title": "reference",\n "fields": {\n "reference": {"type": "autonumber", "label": "Number", "prefix": "CK-", "padding": 3},\n "customer": {"type": "link", "label": "Customer", "table": "customer", "multi": false, "required": true},\n "due_on": {"type": "date", "label": "Pickup on"},\n "pay_by": {"type": "date", "label": "Pay balance by"},\n "lines": {"type": "link", "label": "Cakes", "table": "cake_line", "multi": true, "pair": "order"},\n "total": {"type": "rollup", "label": "Total", "via": "lines", "op": "sum", "field": "amount"},\n "prepaid": {"type": "number", "label": "Prepaid", "notation": {"style": "currency", "currency": "USD"}},\n "balance": {"type": "formula", "label": "Balance", "expression": "{total} - {prepaid}", "notation": {"style": "currency", "currency": "USD"}},\n "paid": {"type": "formula", "label": "Paid", "expression": "{total} > 0 && {balance} <= 0", "output_type": "boolean"},\n "slip": {"type": "files", "label": "Slip"},\n "stage": {"type": "select", "label": "Stage", "default": ["taken"], "options": [\n {"alias": "taken", "label": "Taken", "color": "slate"}, {"alias": "baking", "label": "Baking", "color": "amber"},\n {"alias": "ready", "label": "Ready", "color": "blue"}, {"alias": "collected", "label": "Collected", "color": "green"}\n ]}\n },\n "records": {"subtitle": ["customer"], "figure": "total", "due": ["pay_by"], "settled": "paid", "status": {"field": "stage", "closed": ["collected"]}},\n "checks": {"pay_by_before_pickup": {"on": ["create", "update"], "field": "pay_by",\n "when": "!isNull(record.pay_by) && !isNull(record.due_on)", "fail_when": "record.pay_by > record.due_on",\n "message": "\\"The balance is due on or before the pickup day\\""}}\n },\n {\n "ref": "cake_line", "label": "Cakes on orders", "singular": "Cake", "title": "item",\n "fields": {\n "item": {"type": "link", "label": "Cake", "table": "menu_item", "multi": false, "required": true},\n "order": {"type": "link", "label": "Order", "table": "cake_order", "multi": false, "required": true, "pair": "lines"},\n "quantity": {"type": "number", "label": "Quantity", "required": true, "min": 1},\n "unit_price": {"type": "number", "label": "Unit price", "default_from": "item.price", "notation": {"style": "currency", "currency": "USD"}},\n "amount": {"type": "formula", "label": "Amount", "expression": "{quantity} * {unit_price}", "notation": {"style": "currency", "currency": "USD"}}\n },\n "unique": [["order", "item"]],\n "records": {"subtitle": ["quantity"], "figure": "amount"}\n }\n ]\n}\n```\n\n`stage` is the table\'s status. `party` (`"person"` or `"organization"`) draws a face; `image` is the files field that pictures a row, never the one an Act files its paper in; `settled` is a yes/no that ends the `due` deadlines, so it needs a `due`; `figure` is a number. `unique` is two fields or more no two rows share (one line per cake on an order). A line copies the menu\'s price with `default_from` when made, so a later price change leaves taken orders alone. `min`, `unique` and the check hold on every write, from any app or tool; a check\'s language is `workflows/table-checks`, an effect after any write a table workflow (`workflows/table-workflows`). Every key is in `model/tables`.\n\n## 2. The paper: `set_template`\n\nThe app\'s Act names the template by its alias, made from its name (`order_slip`): the paper comes first.\n\n```\nset_template {"type": "html", "name": "Order slip",\n "template": "<h1>Order {{reference}}</h1><p>{{customer}} \u2014 pickup {{date due_on}}</p>{{#each lines}}<p>{{item}} \xD7 {{quantity}} \u2014 {{money amount \\"USD\\"}}</p>{{/each}}<p>Total {{money total \\"USD\\"}}</p>",\n "variables": {"reference": "string", "customer": "string", "due_on": "string", "lines": "array", "total": "string"}}\n```\n\nEvery paper the owner sends is made, one template each. A slot is named after a field of the record; the order\'s cakes loop under the name of its link to them (`lines`). Fields, loops, layout and papers over many rows: `document_templates`.\n\n## 3. The app and its acts: `set_app`\n\n**Which apps.** Shape the apps to the requirement: an app is scoped to a role\'s work, what that role manages, from the list they open to everything about each row. Where one person runs HR, their app holds the people and, on each person, their contracts, payslips, papers and leave, whatever rhythm each runs at; a business whose payroll, hiring and leave are different people\'s work may want an app for each. Part of one row (a vehicle\'s papers)? A Section or child List on that row\'s page. A value from related rows on the list itself (the newest contract\'s end date)? A lookup field as a column. Work over many rows at once (a month\'s payroll)? An Act on the list\'s rows. Used only while doing another job (the rates a quote is priced from)? A picker on that job\'s lines, price copied by `default_from`. A role that works several lists holds them in its app; a different role, or work another team does on its own, is another app. Never an app per table.\n\n**The view per kind of row**, chosen before writing the List (a plain register needs none):\n\n| The rows are | The treatment | Keys |\n|---|---|---|\n| People or organisations | `records.party`, photo or logo as `image` | `model/records` |\n| Photos people look at | `records.image`; List `layout: "cards"` | `model/cards` |\n| Held on a day people schedule by | List `layout: "calendar"` | `model/calendar` |\n| Booked on a resource over time | List `layout: {"lanes": {"by": \u2026}}`, table `no_overlap` | `model/lanes` |\n| Standing at places on a floor or yard | List `layout: {"plan": \u2026}` | `model/plan` |\n| People across days | List `layout: "roster"`, the cells\' List inside | `model/roster` |\n| Expected against counted, before against after | a List with `compare: {"before": \u2026, "after": \u2026}` | `model/list-node` |\n| Ranked by a figure | List `sort: {"field": \u2026, "desc": true}` | `model/list-node` |\n| Moved through a status (stages) | an Act per move, `status.dates`, Readings | `model/act-node` |\n| Each belonging to another row | a child List on its Record | `model/list-node` |\n| Counted over a period | Tabs over one `period`; Readings | `model/tab-node` |\n| Papers a case gathers | a List with `expect`; an Act\'s `template` | `model/list-node` |\n\nAny other screen: `design/treatments`.\n\nHere one app, the counter\'s job, naming the tables and the template by the aliases above. The register lists the orders; opening one shows a Section per stage, and each Act sets the next stage. The cakes are a child List of the open order, added in place with `create`. Starting to bake asks for the deposit, readying makes the slip, collecting waits for payment.\n\n```json\n{\n "body": {\n "type": "App", "key": "counter", "name": "Counter", "color": "amber",\n "children": [\n {"type": "List", "entity": "cake_order", "filters": ["customer"], "create": ["customer", "due_on", "pay_by"], "children": [\n {"type": "Record", "door": "page", "children": [\n {"type": "Section", "key": "order", "title": "Order", "at": ["taken", "baking"], "children": [\n "due_on",\n "pay_by",\n {"type": "List", "entity": "cake_line", "create": ["item", "quantity"]},\n {"type": "Act", "key": "start_baking", "label": "Start baking", "when": {"stage": ["taken"]}, "requires": ["due_on"], "asks": ["prepaid"], "set": {"stage": "baking"}},\n {"type": "Act", "key": "ready", "label": "Ready", "when": {"stage": ["baking"]}, "set": {"stage": "ready"}, "template": "order_slip", "into": "slip"}\n ]},\n {"type": "Section", "key": "pickup", "title": "Pickup", "at": ["ready"], "children": [\n {"field": "balance", "edit": false},\n {"field": "slip", "edit": false},\n {"type": "Act", "key": "collect", "label": "Collected", "when": {"stage": ["ready"], "paid": true}, "set": {"stage": "collected"}}\n ]}\n ]}\n ]}\n ]\n }\n}\n```\n\n`set_app` answers with a note for what the rows call for that the tree leaves out; these fold with one: a read-only copy of a placed Field, `create`/`remove: false` on a read-only List, its `app` where one app is over its table, a lone text field\'s `unique`, a patch address unlike the node\'s `key`. **What the check refuses**, each with its id (`model/<id>` is its page):\n\n- `record.placed-once`: a Field, child List, Reading, Statement or Act stands once in a Record, whose head draws the title, subtitle, image, status, figure.\n- `register.filter-unshown`: a `filters` entry (at most 3) is a select, member, link, date, yes/no or number the rows show, or a formula of such; never text, and the status is the chips already.\n- `register.columns-budget`, `block.columns-budget`: `columns` fit the room a desk leaves beside the name, status, deadline and figure.\n- `act.requires-drawn`: an Act\'s `requires` field is drawn in its Section or asked by it.\n- `app.logs`: a further List at the App\'s root is a log (at most 3) with its own `key`, over another table than the register\'s.\n- `act.makes-or-runs`: an Act makes a `template` or runs a `workflow`.\n- `reading.list-app`: a List\'s `app` has its register over that List\'s table.\n- `block.list-opens`: a read-only child List opens its rows in the drawer, so its table is one a List of the app draws, or its `app` names the app that works it.\n- A Field\'s `label` or `when` stands only on a gallery or prose Field (files, markdown text, or `block: true`).\n- `entity.unique`: a `unique` set holds two fields or more of one value each (text, number, date, single select, one-row link).\n- `model.names-declared`: each alias a key names exists (a table, field, option, Act `key`, template).\n- A Query\'s `sort` is `{"field": \u2026, "desc": true}` (`desc` optional) or a list of them, beside `fields`.\n- A workflow writes a link as ids, `[x.id]`; a whole record or bare value is refused.\n\nThen `set_access` gives the app to the people whose job it is: a `principal_type` of `member` (id from `get_workspace`, `kind: "member"`), `member_group` (an admin lists them: `kind: "group"`) or `organization`, a `role` of `user` or `manager`; none loses access.\n\n```\nset_access {"resources": [{"resource_type": "app", "resource_id": "app_\u2026"}], "principals": [{"principal_type": "member", "principal_id": "<member id>", "role": "user"}]}\n```\n\n## 4. What the owner will ask an agent: Query and Workflow nodes\n\nRead the brief again for what the business asks its assistant again and again: a figure or list whose definition is its own (orders waiting for pickup), an operation (chase overdue balances). Declare each as a Query or Workflow node of the app, with a `description` for an agent that has not seen the screen (`agent_surface` \xA7 Declaring more); a connected service (MISA, a carrier) is reached by a Workflow node\'s step (`workflows`); a question asked once stays on `aggregate_records`.\n\n## 5. The owner\'s rows: `create_records`\n\nThe owner\'s own list goes in as rows, in the batches the tool allows: the customers and menu first, then the orders, linked by the ids those calls return, then each order\'s cakes, linked to the order and a menu item. A row created past the first stage (`records.status.stages`) is refused unless that owned stage\'s owner or a workspace admin makes it, so load the owner\'s rows as an admin. Example rows the papers ask for and do not list come from the owner\'s lists and sample, marked as examples, each created at the stage it should show; pressing rows proves the app and never spreads examples across steps; a placeholder ("Demo customer") is never made where the owner\'s own rows exist. The value each field type takes is in `field_values`.\n\n```\ncreate_records {"table_id": "menu_item", "field_keys": ["name", "price"], "rows": [["Lemon cake", 24], ["Chocolate cake", 28]]}\ncreate_records {"table_id": "customer", "field_keys": ["name"], "rows": [["Ana Lopez"]]}\ncreate_records {"table_id": "cake_order", "field_keys": ["customer", "due_on", "pay_by"], "rows": [["<Ana\'s record id>", "2031-03-14", "2031-03-12"]]}\ncreate_records {"table_id": "cake_line", "field_keys": ["order", "item", "quantity"],\n "rows": [["<that order\'s record id>", "<Lemon cake\'s record id>", 2]]}\n```\n\n## 6. Verify: `run_app_workflow`, `screenshot_app`\n\nMove the owner\'s own rows through the app as their person would, in stage order. An Act runs as `act_<key>` (or under the alias of the `workflow` it names), handed what it asks; dry-run first, then press. To prove a rule, send the write with `dry_run: true`; never make a test row or switch a check off. A row ends at the stage the owner says it is at, so press each only that far; where the owner gave no rows, ask for a few.\n\n```\nrun_app_workflow {"app_id": "app_\u2026", "alias": "act_start_baking", "inputs": {"record_id": "rec_\u2026", "prepaid": 20}, "dry_run": true}\nrun_app_workflow {"app_id": "app_\u2026", "alias": "act_start_baking", "inputs": {"record_id": "rec_\u2026", "prepaid": 20}}\nscreenshot_app {"app_id": "app_\u2026"}\n```\n\nFix a failure in the table or app that causes it. Leave standing instructions with `set_workspace` (`instructions`, one entry per rule): who the business is, the words its people use, what any agent working here must keep to. As an admin, invite the people the brief names with `set_member` (`members` keyed by email, role `member` or `admin`); where the tool is missing, say whom to invite. Then show the owner what you built, job by job, each paper they sent and what makes it, with the choices you made for them to change.\n';
|
|
1752
|
+
var first_build_default = '# First build \u2014 a whole workspace, call by call\n\nA bakery takes custom-cake orders at the counter. An order is a customer and the cakes on it, one line per cake from the menu; it moves taken, baking, ready, collected, and a slip of the cakes prints when it is ready. Six steps, in the order every workspace is built.\n\nStep 0, before the first write: ask the owner once, in one message, only what you cannot settle yourself. Usually: their papers and lists \u2014 a photo, a spreadsheet or a file is fine; they answer most questions (what they send and receive, customers, items, prices) \u2014 and what no paper tells you that would change the tables or who does which step. The owner answers by hand, so every question costs them time: ask nothing they gave already or a paper settles. Each question is one sentence, no sub-questions or bullets under it; nothing else in that message. Wait for the answer, then build; never build first and ask after. Never ask about stages or design: take a default for everything else and list it in your report so the owner can correct it. Load the owner\'s own rows. `get_workspace` shows what already exists; what no one could guess from the data goes into a knowledge doc with `set_knowledge` (`knowledge_docs`).\n\n## 1. Tables and their checks: `set_tables`\n\nOne call, linked tables together; a call names a table by its alias, a new table in the call by its `ref`, which becomes its alias; a link names the other table so. Three kinds appear: a book lines are chosen from (`menu_item`), the record the work moves (`cake_order`) and its lines (`cake_line`). One table per kind of thing, one column per fact, a link wherever one record points at another. A status is the stage of the work, six options or fewer; a touch ("called") is a log row, a money fact ("paid in full") a formula, a quantity a number with its unit in `notation`. Every table people work states `singular` (an app with an Add over a table lacking it is refused), `title` (who or what a row is; a code only where nothing else names it) and `records` (an app over a table without it is refused).\n\n```json\n{\n "tables": [\n {\n "ref": "customer", "label": "Customers", "singular": "Customer", "title": "name",\n "fields": {"name": {"type": "text", "label": "Name", "required": true}},\n "records": {"party": "person"}\n },\n {\n "ref": "menu_item", "label": "Menu", "singular": "Menu item", "title": "name",\n "fields": {\n "name": {"type": "text", "label": "Cake", "required": true},\n "photo": {"type": "files", "label": "Photo"},\n "price": {"type": "number", "label": "Price", "required": true, "notation": {"style": "currency", "currency": "USD"}}\n },\n "records": {"figure": "price", "image": "photo"}\n },\n {\n "ref": "cake_order", "label": "Cake orders", "singular": "Cake order", "title": "reference",\n "fields": {\n "reference": {"type": "autonumber", "label": "Number", "prefix": "CK-", "padding": 3},\n "customer": {"type": "link", "label": "Customer", "table": "customer", "multi": false, "required": true},\n "due_on": {"type": "date", "label": "Pickup on"},\n "pay_by": {"type": "date", "label": "Pay balance by"},\n "lines": {"type": "link", "label": "Cakes", "table": "cake_line", "multi": true, "pair": "order"},\n "total": {"type": "rollup", "label": "Total", "via": "lines", "op": "sum", "field": "amount"},\n "prepaid": {"type": "number", "label": "Prepaid", "notation": {"style": "currency", "currency": "USD"}},\n "balance": {"type": "formula", "label": "Balance", "expression": "{total} - {prepaid}", "notation": {"style": "currency", "currency": "USD"}},\n "paid": {"type": "formula", "label": "Paid", "expression": "{total} > 0 && {balance} <= 0", "output_type": "boolean"},\n "slip": {"type": "files", "label": "Slip"},\n "stage": {"type": "select", "label": "Stage", "default": ["taken"], "options": [\n {"alias": "taken", "label": "Taken", "color": "slate"}, {"alias": "baking", "label": "Baking", "color": "amber"},\n {"alias": "ready", "label": "Ready", "color": "blue"}, {"alias": "collected", "label": "Collected", "color": "green"}\n ]}\n },\n "records": {"subtitle": ["customer"], "figure": "total", "due": ["pay_by"], "settled": "paid", "status": {"field": "stage", "closed": ["collected"]}},\n "checks": {"pay_by_before_pickup": {"on": ["create", "update"], "field": "pay_by",\n "when": "!isNull(record.pay_by) && !isNull(record.due_on)", "fail_when": "record.pay_by > record.due_on",\n "message": "\\"The balance is due on or before the pickup day\\""}}\n },\n {\n "ref": "cake_line", "label": "Cakes on orders", "singular": "Cake", "title": "item",\n "fields": {\n "item": {"type": "link", "label": "Cake", "table": "menu_item", "multi": false, "required": true},\n "order": {"type": "link", "label": "Order", "table": "cake_order", "multi": false, "required": true, "pair": "lines"},\n "quantity": {"type": "number", "label": "Quantity", "required": true, "min": 1},\n "unit_price": {"type": "number", "label": "Unit price", "default_from": "item.price", "notation": {"style": "currency", "currency": "USD"}},\n "amount": {"type": "formula", "label": "Amount", "expression": "{quantity} * {unit_price}", "notation": {"style": "currency", "currency": "USD"}}\n },\n "unique": [["order", "item"]],\n "records": {"subtitle": ["quantity"], "figure": "amount"}\n }\n ]\n}\n```\n\n`stage` is the table\'s status. `party` (`"person"` or `"organization"`) draws a face; `image` is the files field that pictures a row, never the one an Act files its paper in; `settled` is a yes/no that ends the `due` deadlines, so it needs a `due`; `figure` is a number. `unique` is two fields or more no two rows share (one line per cake on an order). A line copies the menu\'s price with `default_from` when made, so a later price change leaves taken orders alone. `min`, `unique` and the check hold on every write, from any app or tool; a check\'s language is `workflows/table-checks`, an effect after any write a table workflow (`workflows/table-workflows`). Every key is in `model/tables`.\n\n## 2. The paper: `set_template`\n\nThe app\'s Act names the template by its alias, made from its name (`order_slip`): the paper comes first.\n\n```\nset_template {"type": "html", "name": "Order slip",\n "template": "<h1>Order {{reference}}</h1><p>{{customer}} \u2014 pickup {{date due_on}}</p>{{#each lines}}<p>{{item}} \xD7 {{quantity}} \u2014 {{money amount \\"USD\\"}}</p>{{/each}}<p>Total {{money total \\"USD\\"}}</p>",\n "variables": {"reference": "string", "customer": "string", "due_on": "string", "lines": "array", "total": "string"}}\n```\n\nEvery paper the owner sends is made, one template each. A slot is named after a field of the record; the order\'s cakes loop under the name of its link to them (`lines`). Fields, loops, layout and papers over many rows: `document_templates`.\n\n## 3. The app and its acts: `set_app`\n\n**Which apps.** One per role\'s work, shaped to the requirement: the list that role opens, and everything about each row on its page whatever its rhythm. One HR person\'s app holds the people and, on each, their contracts, payslips and leave; a business where payroll and hiring are different people\'s work may want an app each. A related row\'s value on the list (the newest contract\'s end)? A lookup column. Work over many rows (a month\'s payroll)? An Act on the list\'s rows. Used only inside another job (the rates a quote is priced from)? A picker on its lines, price copied by `default_from`. Never an app per table.\n\n**The view per kind of row**, chosen before writing the List (a plain register needs none):\n\n| The rows are | The treatment | Keys |\n|---|---|---|\n| People or organisations | `records.party`, photo or logo as `image` | `model/records` |\n| Photos people look at | `records.image`; List `layout: "cards"` | `model/cards` |\n| Held on a day people schedule by | List `layout: "calendar"` | `model/calendar` |\n| Booked on a resource over time | List `layout: {"lanes": {"by": \u2026}}`, table `no_overlap` | `model/lanes` |\n| Standing at places on a floor or yard | List `layout: {"plan": \u2026}` | `model/plan` |\n| People across days | List `layout: "roster"`, the cells\' List inside | `model/roster` |\n| Expected against counted, before against after | a List with `compare: {"before": \u2026, "after": \u2026}` | `model/list-node` |\n| Ranked by a figure | List `sort: {"field": \u2026, "desc": true}` | `model/list-node` |\n| Moved through a status (stages) | an Act per move, `status.dates`, Readings | `model/act-node` |\n| Each belonging to another row | a child List on its Record | `model/list-node` |\n| Counted over a period | Tabs over one `period`; Readings | `model/tab-node` |\n| Papers a case gathers | a List with `expect`; an Act\'s `template` | `model/list-node` |\n\nAny other screen: `design/treatments`.\n\nHere one app, the counter\'s job, naming the tables and the template by the aliases above. The register lists the orders; opening one shows a Section per stage, and each Act sets the next stage. The cakes are a child List of the open order, added in place with `create`. Starting to bake asks for the deposit, readying makes the slip, collecting waits for payment.\n\n```json\n{\n "body": {\n "type": "App", "key": "counter", "name": "Counter", "color": "amber",\n "children": [\n {"type": "List", "entity": "cake_order", "filters": ["customer"], "create": ["customer", "due_on", "pay_by"], "children": [\n {"type": "Record", "door": "page", "children": [\n {"type": "Section", "key": "order", "title": "Order", "at": ["taken", "baking"], "children": [\n "due_on",\n "pay_by",\n {"type": "List", "entity": "cake_line", "create": ["item", "quantity"]},\n {"type": "Act", "key": "start_baking", "label": "Start baking", "when": {"stage": ["taken"]}, "requires": ["due_on"], "asks": ["prepaid"], "set": {"stage": "baking"}},\n {"type": "Act", "key": "ready", "label": "Ready", "when": {"stage": ["baking"]}, "set": {"stage": "ready"}, "template": "order_slip", "into": "slip"}\n ]},\n {"type": "Section", "key": "pickup", "title": "Pickup", "at": ["ready"], "children": [\n {"field": "balance", "edit": false},\n {"field": "slip", "edit": false},\n {"type": "Act", "key": "collect", "label": "Collected", "when": {"stage": ["ready"], "paid": true}, "set": {"stage": "collected"}}\n ]}\n ]}\n ]}\n ]\n }\n}\n```\n\n`set_app` answers with a note for what the rows call for that the tree leaves out; these fold with one: a read-only copy of a placed Field, `create`/`remove: false` on a read-only List, its `app` where one app is over its table, a lone text field\'s `unique`, a patch address unlike the node\'s `key`. **What the check refuses**, each with its id (`model/<id>` is its page):\n\n- `record.placed-once`: a Field, child List, Reading, Statement or Act stands once in a Record, whose head draws the title, subtitle, image, status, figure.\n- `register.filter-unshown`: a `filters` entry (at most 3) is a select, member, link, date, yes/no or number the rows show, or a formula of such; never text, and the status is the chips already.\n- `register.columns-budget`, `block.columns-budget`: `columns` fit the room a desk leaves beside the name, status, deadline and figure.\n- `act.requires-drawn`: an Act\'s `requires` field is drawn in its Section or asked by it.\n- `app.logs`: a further List at the App\'s root is a log (at most 3) with its own `key`, over another table than the register\'s.\n- `act.makes-or-runs`: an Act makes a `template` or runs a `workflow`.\n- `reading.list-app`: a List\'s `app` has its register over that List\'s table.\n- `block.list-opens`: a read-only child List opens its rows in the drawer, so its table is one a List of the app draws, or its `app` names the app that works it.\n- A Field\'s `label` or `when` stands only on a gallery or prose Field (files, markdown text, or `block: true`).\n- `entity.unique`: a `unique` set holds two fields or more of one value each (text, number, date, single select, one-row link).\n- `model.names-declared`: each alias a key names exists (a table, field, option, Act `key`, template).\n- A Query\'s `sort` is `{"field": \u2026, "desc": true}` (`desc` optional) or a list of them, beside `fields`.\n- A workflow writes a link as ids, `[x.id]`; a whole record or bare value is refused.\n\nThen `set_access` gives the app to the people whose job it is: a `principal_type` of `member` (id from `get_workspace`, `kind: "member"`), `member_group` (an admin lists them: `kind: "group"`) or `organization`, a `role` of `user` or `manager`; none loses access.\n\n```\nset_access {"resources": [{"resource_type": "app", "resource_id": "app_\u2026"}], "principals": [{"principal_type": "member", "principal_id": "<member id>", "role": "user"}]}\n```\n\n## 4. What the owner will ask an agent: Query and Workflow nodes\n\nRead the brief again for what the business asks its assistant again and again: a figure or list whose definition is its own (orders waiting for pickup), an operation (chase overdue balances). Declare each as a Query or Workflow node of the app, with a `description` for an agent that has not seen the screen (`agent_surface` \xA7 Declaring more); a connected service (MISA, a carrier) is reached by a Workflow node\'s step (`workflows`); a question asked once stays on `aggregate_records`.\n\n## 5. The owner\'s rows: `create_records`\n\nThe owner\'s own list goes in as rows, in the batches the tool allows: the customers and menu first, then the orders, linked by the ids those calls return, then each order\'s cakes, linked to the order and a menu item. A row created past the first stage (`records.status.stages`) is refused unless that owned stage\'s owner or a workspace admin makes it, so load the owner\'s rows as an admin. Example rows the papers ask for and do not list come from the owner\'s lists and sample, marked as examples, each created at the stage it should show; pressing rows proves the app and never spreads examples across steps; a placeholder ("Demo customer") is never made where the owner\'s own rows exist. The value each field type takes is in `field_values`.\n\n```\ncreate_records {"table_id": "menu_item", "field_keys": ["name", "price"], "rows": [["Lemon cake", 24], ["Chocolate cake", 28]]}\ncreate_records {"table_id": "customer", "field_keys": ["name"], "rows": [["Ana Lopez"]]}\ncreate_records {"table_id": "cake_order", "field_keys": ["customer", "due_on", "pay_by"], "rows": [["<Ana\'s record id>", "2031-03-14", "2031-03-12"]]}\ncreate_records {"table_id": "cake_line", "field_keys": ["order", "item", "quantity"],\n "rows": [["<that order\'s record id>", "<Lemon cake\'s record id>", 2]]}\n```\n\n## 6. Verify: `run_app_workflow`, `screenshot_app`\n\nMove the owner\'s own rows through the app as their person would, in stage order. An Act runs as `act_<key>` (or under the alias of the `workflow` it names), handed what it asks; dry-run first, then press. To prove a rule, send the write with `dry_run: true`; never make a test row or switch a check off. A row ends at the stage the owner says it is at, so press each only that far; where the owner gave no rows, ask for a few.\n\n```\nrun_app_workflow {"app_id": "app_\u2026", "alias": "act_start_baking", "inputs": {"record_id": "rec_\u2026", "prepaid": 20}, "dry_run": true}\nrun_app_workflow {"app_id": "app_\u2026", "alias": "act_start_baking", "inputs": {"record_id": "rec_\u2026", "prepaid": 20}}\nscreenshot_app {"app_id": "app_\u2026"}\n```\n\nFix a failure in the table or app that causes it. Leave standing instructions with `set_workspace` (`instructions`, one entry per rule): who the business is, the words its people use, what any agent working here must keep to. As an admin, invite the people the brief names with `set_member` (`members` keyed by email, role `member` or `admin`); where the tool is missing, say whom to invite. Then show the owner what you built, job by job, each paper they sent and what makes it, with the choices you made for them to change.\n';
|
|
1753
1753
|
|
|
1754
1754
|
// docs/design.md
|
|
1755
1755
|
var design_default = "# Designing an app\n\nThe treatment each thing a job reads takes. Every prop named here is a prop of a component of the app tree, and the page of the `model` reference stating a treatment's props stands beside it. A treatment worked through in an app of a complete workspace is a page at `examples/<treatment>`. The method, the apps a business needs and the checks a tree meets are in `first_build`.\n\nThe common treatments are the chooser in `first_build`, \xA7 3; every treatment is listed at `design/treatments`.\n\nAn app is a tree of components: a List of one table, the Record a row opens, its Sections, the Acts that move it. The\ntree states COMPOSITION \u2014 which fields, lists and acts each job's screen holds. How each piece looks is the platform's,\none way per concept, and no prop changes it. A component bound to a table draws well with no props, because its defaults\ncome from the table's facts (`records`, the stage, the write rules): a node the tree leaves out draws that default \u2014 a\nList's Readings band, the stage buttons, the picture, a Record's designed Sections, a head kept to its budget. State a\nnode to change its default, or `false` at it to turn it off (`\"readings\": false`, `\"row\": { \"status\": { \"edit\": false } }`,\n`\"row\": { \"image\": false }`). A Field the screens draw is a control, a save, unless it states `edit: false`: an entity drawn read-only gets none, and a stepped status is never a default edit, since Acts move it. A List left as a table is drawn as one, even where a calendar, a face or a picture would\nanswer the reader at a glance, and that is the most common failure of an app: the answer of `set_app` notes what the\nrows call for that no default draws (rows in time, a template's Act, a timeline), and the tree states the ones the job\nneeds.\n\n## Treatments\n\nA treatment named in a section below is one the answer of `set_app` notes when the rows call for it and neither the\ntree nor a platform default draws it, naming the node that would state it. Nothing is written into the tree for the\nauthor: state the ones the job needs. A note refuses nothing.\n\n| The rows are | The treatment | Its props |\n|---|---|---|\n| People or organisations | `records.party`, with a photo or logo as its `image` | `model/records` |\n| Things with a photo, a scan or a paper | `records.image`; List `layout: \"cards\"` where the picture is how a row is found | `model/records`, `model/cards` |\n| Read by their day | List `layout: \"calendar\"`; on a record, a child List with `layout: {\"agenda\": \u2026}` | `model/calendar`, `model/layouts` |\n| Booked on a resource | List `layout: {\"lanes\": {\"by\": \u2026}}`, and the table's `no_overlap` | `model/lanes`, `model/write-rules` |\n| A log of what happened | a child List with `layout: \"timeline\"` | `model/layouts` |\n| Opened to be worked on | the Record's Sections, in the order the work reaches them \u2014 `at` where it moves in steps; fields only one kind of row holds (a delay's cause, a safety issue's hours lost) in a Section whose `when` names that kind | `model/section-node` |\n| Rows that each belong to another row | a child List on that row's Record, with `create` and `remove`; a List with `same` or `app` where read-only, for rows another app works | `model/list-node` |\n| A related row's value on the list (the newest contract's end date) | a `lookup` field of the table, ordered, as a column | `model/lookup` |\n| Moving through a status | an Act per move, `status.dates` for the moment of each stage, and the register's Readings | `model/act-node`, `model/status`, `model/reading-node` |\n| Counted over a period | the register's Tabs over one `period`; a Tab's `totals` for a child's sums before, during and through it; an open Tab's `where` reads only what was true at the period's end, never a status the row moves on from | `model/tab-node` |\n| Papers a case gathers | a List with `expect`; the papers made, an Act's `templates` | `model/list-node`, `model/act-node` |\n| A plan of spans | List `layout: {\"gantt\": {\"start\": \u2026}}` | `model/gantt` |\n| Stood at a place on a plan, flat or in 3D \u2014 pallets, bins | List `layout: {\"plan\": \u2026}`; places state `plan_place` | `model/plan` |\n| Checked item by item against a standard | a List with `checklist: {\"photo\": \u2026, \"clause\": \u2026, \"failed\": [\u2026], \"passed\": \u2026}`: each row a mark (passed at `closed`, failed at a `failed` option, or by the yes/no `passed` names \u2014 a measured verdict, no task or status needed), its photo and the clause it is checked against; with `expect` on the standard's items, each item is a line marked the same way and filled by the block's columns | `model/list-node` |\n| A place before and after the work | a List with `compare: {\"before\": \u2026, \"after\": \u2026}`: two files fields read side by side, or two value fields read as one struck-then-new column | `model/list-node` |\n| Spots on a drawing | `records.pin: {\"on\": \u2026, \"x\": \u2026, \"y\": \u2026}` on the spots' table: a record listing rows that link to them draws the picture with a numbered pin at each; the picture is the `on` row's `records.image` | `model/record-places-pins-and-cadence` |\n| Photos that must be taken on site | `records.on_site: {\"photos\": [\u2026]}`: a photo taken through the camera keeps where the device stood, and a record holding one taken away says so | `model/record-places-pins-and-cadence` |\n| The real size of the thing (a carton, a piece of furniture, a pallet) | `records.size: {\"across\": \u2026, \"along\": \u2026, \"up\": \u2026}`: three number fields in one length unit, read as one fact \"400 \xD7 300 \xD7 250 mm\" after the subtitle; a plan register draws each box at that size | `model/record-places-pins-and-cadence`, `model/plan` |\n| A number painted on the thing, or a barcode or QR code on it | `records.scan: \"iso6346\"` or `\"text\"` (read off one photo by an agent), or `\"code\"` (decoded on the device, no agent): the title read on a phone, beside each search matching it | `model/record-places-pins-and-cadence` |\n| Who did what on which day | List `layout: \"roster\"` with the List of its cells inside | `model/roster` |\n| Someone with no seat signs off one record (a buyer approves a proof, a client signs a handover) | the Record's `Outside`: the Fields and Lists the link shows as they are, and Acts that each move the status out of its `at` \u2014 a link a member makes of the record, never a seat | `model/outside-node` |\n| A record whose page is its own arrangement of pieces the app already states | Record `compose` | `model/composed-page` |\n| Steps people drop out of between | a Reading with `steps` on the record whose child rows count them | `model/reading-node` |\n| A record's own numbers nobody keys, read at a glance | consecutive Readings with no `entity` (two to eight tiles) | `model/reading-node` |\n| Rows that are each a place a record is sent | a List with `expect` on the link to those places and `post`: each ticked, the post written there and read as its feed shows it | `model/publish-desk` |\n| Papers an agent reads, where the owner asked for one | an Act's `intake` with `fills` | `model/act-node` |\n| Calls and visits | an Act's `record` | `model/act-node` |\n| A tool on a record that the model cannot draw (a drawing editor, a folded-box preview) | a custom-code app over the same table, opened by an Act's `open` | `model/act-node` |\n| Changes a workflow proposes that a person approves row by row \u2014 a price list applied, a statement matched to what is owed | an Act's `workflow` with `review`, its body returning a changeset in `data.review` | `model/act-node` |\n| Documents covering lines | a List with `under` | `model/list-node` |\n| Rows on file a record takes in | a List with `pick` | `model/list-node` |\n| One owner's question across jobs | an App of Tabs and Readings and no register | `model/app-node` |\n\n### Faces and pictures\n\n#### Party\n\n`records.party` is `\"person\"` where the rows are people (a contact, a patient, a driver) and\n`\"organization\"` where they are companies (a customer, a supplier, a carrier). Every surface drawing one of\nits rows \u2014 the register's row, a link's chip, a picker's option, the record's header \u2014 then draws a face:\nits `image` (a photo, a logo), else its initials. Without it, a person is drawn as a thing. A workspace\nmember is a `member` field, and is drawn by their face already. Worked: `examples/party`.\n\n#### Image\n\n`records.image` names the files field that pictures a row \u2014 a product's photo, a damage photo, a\nscan, a receipt. Every row of it then leads with its picture, or its paper's preview, wherever it is drawn;\na link to it previews the file. Never a field an Act keeps the paper it makes in (`into`). List\n`layout: \"cards\"` where the picture is how a row is found. A files field the record keeps as a folder of\npapers is drawn as a Field, read whole. Worked: `examples/cards`.\n\n#### A link opens its row\n\nOn a record, a link to one row opens its peek: a drawer on a desk, a sheet on a phone, reading the\nrow's face \u2014 mark, name, status, line and its figure by its field's name \u2014 and nothing to edit. A link read in a\nrow's cell or on its line opens from its name; a link the record keeps as a field rests as its picker, and\nits way out stands at the field's label where the row has somewhere to go. The peek's one act leaves for\nthe app whose register is that table's rows \u2014 the first, in the member's own order of apps, that the member\ncan open \u2014 at the row's address, or goes to the row inside this app where the table is the app's own,\none of its Books' or a log's, in that list \u2014 a log's rows only from their own app. With no such app the peek has no act, and a link answered in what an Act\nasks opens no peek. No prop states it: give the table an app of its own and\nits links lead there. The app opened decides what the member reads of the row; a row it withholds\nlands them on its not-found.\n\n### Rows in time\n\nRows that each stand on a day, or hold a run of days or hours, are read where they stand in time \u2014 never\nas two date columns. A row holds a span when its `records` line (title and subtitle) holds two dates, its\n`records.frees` names the day it ends, or the table's `no_overlap` books it on a row its `by` links to (a\nchair, a machine). A `no_overlap` by a person or a value alone keeps one row at a time each \u2014 a member's periods in\nturn \u2014 read by what they hold.\n\n#### Calendar\n\n`layout: \"calendar\"`: the rows by their day \u2014 a month, a week, a day or a list; an entry at its\nhour where its date holds one, over its span where its line holds a second date. An entry pressed opens a card on\nit: the row, Delete, its Acts, Reschedule (the day alone moves; its hour and its length stay) and Open. Worked:\n`examples/calendar`.\n\n#### Lanes\n\n`layout: {\"lanes\": {\"by\": \u2026}}` with `by` naming a one-row link whose target's rows are the lanes (a chair, a\nmachine, a room), each drawn even when empty: every row a block from the first date on its line to the\nsecond, and a free stretch adds a row there. `loads` reads what a lane carries against what it holds. A\n`no_overlap` `by` the same link refuses two rows on one resource at once. Worked: `examples/lanes`.\n\n#### Gantt\n\n`layout: {\"gantt\": {\"start\": \u2026}}` (and `end`, else the table's first `due`): one bar a row over\nits run of days \u2014 a shipment, a hire, a task of a project \u2014 read against today. `milestones` marks\ndates on the bar, `planned` the plan under it, `progress` how far it is done, `after` the rows it waits on;\nthe List's `group` is its lanes. Worked: `examples/gantt`.\n\n#### Roster\n\n`layout: \"roster\"` with a List inside it over the child whose rows fill the days \u2014 one link back to the\nregister's table and a date on its line: what each row did on each day (shifts, runs, attendance). With\n`expect` (and `of`) on the cells the columns are the options of a select instead of days: which papers each row holds.\nWorked: `examples/roster`.\n\n#### Agenda\n\nA child List with `layout: {\"agenda\": {\u2026}}` on a record: a child's planned entries by day, soonest first, today marked, each with its\npicture; within a day by the hour, or by a single select of the part of the day. `start` counts Day 1 from a\ndate of the record (a trip, a course).\n\n#### Timeline\n\nA child List with `layout: \"timeline\"` on a record: a child read as a log of dated entries, newest first, with a composer \u2014 the\ncalls, visits and notes about the record, each with who and when, and its files where the child's `image`\nnames them. Entries planned ahead are an agenda; the status's moves are its history. Worked:\n`examples/party`.\n\n### Moves and numbers\n\n#### Moves\n\nRows a status stages towards its `closed` options move by Acts: each moves one option on to the next (`when` the\nstatus holds it, `set` to the next), at the foot of the Section whose work it ends. A status with nothing closed is a\ncondition a person sets. A value decided as a row reaches an outcome \u2014 why it was lost, refused, voided \u2014 is that\nAct's `asks`, never a field the reader must remember to fill. An Act reaching past the workspace \u2014 sending an invoice\nto a service, a letter by mail \u2014 runs its `workflow`, and what the workflow keeps from the answer (an invoice's id,\nits link) is named in the Act's `set`. A stage the work only steps aside into from one step\nand leaves only back to it \u2014 a pause, a repair \u2014 is a detour: its option and its Section stand before that step,\nentered by its own Act; listed after, it reads as the work's next step.\n\n#### Stages\n\nName each job's work plan by its shape: a status a person sets; a pipeline, where who holds the row and for how long\nmatter; or tasks, where only how many are done counts (`records.task`). A pipeline states each stage once in\n`records.status.stages` \u2014 its `owner` (a member field, a link's member field, or `{ \"group\": <id> }`, a member group) and its target\n`within` days \u2014 and the server holds it: only the owner's Acts move the row on, and a row created at a stage past the first is a move into that stage from nothing, which the stage's owner, an unowned stage's writer or a workspace admin (loading history) may make, on every path a row is written by, and a level an amount skips stands\nnamed with why. A move forward passes each owned stage between that binds the row, each its owner's. A stage's `when`\nnames the rows it binds, held as an Act's `when` holds them on the row's own fields, as the row stood before a write\nand as the write leaves it; with none, it binds every row. Expense claims over a limit wait for the finance group while\nsmaller ones go straight to payment: `\"finance_review\": { \"owner\": { \"group\": <id> }, \"when\": { \"amount\": { \"gt\": 5000000 } } }`.\nA row at a stage leaves it by its owner whatever its `when`; `dropped` names the closed options\nthat leave the work undone (cancelled, rejected), which whoever holds the row moves it to from any stage. A stage's owner gates who moves a row out of it; who may move a row into a stage (\"only a bookkeeper marks an invoice paid\") is a check. Late rows, rows due soon, a warning the line draws and rows waiting on the reader stand first in a\nregister; a register whose job is a strict order, a first-come queue, states `attention: false`.\n\n#### Stage dates and history\n\nWho changed what and when is the platform's audit trail, never a table and never shown by an app. When a stage's moment matters\nto the business \u2014 reported, filtered, printed \u2014 it is a date on the row that `records.status.dates` stamps as the row\nenters the option, whoever writes: the one way a status is timed, and the moment a stage's `within` runs from (each stage, and\neach open option of a status with stages, naming none gets `<option>_at`). A straight-line process is one select of its steps in order, each dated; a move whose\nday may already be past `asks` that date with `starts: \"today\"`. `records.status.history` names a child the record write path appends one row to per\nstatus move (its link to the row, the new status, the moment and who) for a business that reports on every move: moves\nper person, or time in a stage a row re-enters. An event with facts of its own \u2014 a reason, an amount, a signature \u2014 is\nits own table, made by the act that records it. The rail reads the history. Worked: `examples/approval`, whose history is kept because a group's\nstage is signed once per person, read off who moved the row.\n\n#### Readings\n\nA List's Readings stand above the rows and read the rows under the open tab, never narrowed by the controls below them. Lead with the job's headline number \u2014\nits exception as a formula's yes/no in a Reading's `where` (what is short), never a stage the\nstatus chips already count, nor the overdue rows the register's summary line counts from `records.due` \u2014 then at most the mix (`by` a field) and the movement (`by` a date), varied by job. A movement over a date is stated, never defaulted, and so is a sum: a status register with no single select to split by gets no default band.\nA grid (`by` and `across`) reads on a dashboard. A dashboard \u2014 an App of Readings and no register \u2014 answers one owner's question across jobs, each\nReading windowed by the period the reader picks, and a List hands over the rows to act on. A\ndashboard of many Readings states Tabs (2 to 5) and each Reading the `tab` it stands under, so one sheet draws at a time. A\nRecord's Readings read its own child rows. Sums of each row's child rows are a Tab's `totals`. Worked: `examples/approval`, `examples/dashboard`.\n\n#### Funnel\n\nA Reading with `steps` reads a record's child rows as steps, each a number column summed over them \u2014 a week's channels\nfrom impressions to bookings, an order's lines from ordered to shipped. It states how much of the first step\neach later one keeps, the share lost between two neighbours, and the last over the first. State two to six\n`steps` in one unit; where the path turns \u2014 reach, then conversion \u2014 state two Readings one straight after the\nother, so the second's bars are read over its own first step: they draw on one grid, every bar starting at one\nedge and every value ending at another, each under its own label. A step no row holds a number in reads as not measured: leave it empty, never a zero nobody measured.\n\n#### Priced lines\n\nA quote's items, an estimate's works, a repair sheet's damages are lines priced off a book: a table whose\n`records` names each item, puts the code people type on its line and its price as its `figure`. The line links to\nit, asks the link and its quantities in its List's `create`, and copies the price (`default_from`), so the quote\nkeeps what was quoted when the book moves. A line's amount is a formula, the record's total a rollup over the rows\nthe List shows \u2014 what is not paid for is a second rollup, never a filter on the first, so each group subtotals.\nGroup the lines by the parts the record is quoted in (a container, a room, a leg), a field the line asks. A book worked only while a quote is written is the picker on the quote's lines; a book with a keeper, Acts or a history of its own is\nan app (`first_build` \xA7 3).\n\n### The record\n\n#### Sections\n\nThe Record's Sections, in the order the work reaches them: the fields people write, a Field per paper the record\nkeeps, a child List per child, the Acts at each one's foot. A register's `columns` are in the order the job reads them, as many as fit a desk; what is read only on the way deeper is the Record's, and a figure that is final only from a stage is a Field of that stage's Section, never a column read on every row at every stage. A register laid out in time (calendar, lanes, roster) opens the\ndefault record: every field a person writes. `compose` is not for a record a standard Section order would\ndo, and never for behaviour, which belongs in an Act.\n\n#### Children\n\nRows that each belong to one row of another table (a one-row link to it) are drawn under that row: a List on\nits Record, `layout: \"timeline\"` where they are a log, `layout: {\"agenda\": \u2026}` where they are planned on a day and within it, a roster of\nthem, or lanes of them by that link. Which children a record lists is the author's \u2014 one of them at least. A List\nlisting only some of them narrows by its `where`: a select (one leg's fees), or a yes/no \u2014 a formula's for what is\nstill owed \u2014 on a List that adds no rows. Rows on\nfile before the row they come to belong to \u2014 a job's cost lines before the supplier bill over them \u2014 are picked into\nit: a List with `pick`, often with no `create` and no `remove`.\n\n### Papers\n\n#### Expect\n\nA List with `expect` naming the child's title \u2014 a single select of kinds, or a one-row link to a\ncatalog: one line per kind, the ones not yet filled standing empty, filled in place. `of` narrows the kinds\nto the record's own multi-select of those it needs. The papers a case gathers, the fees a record knows it\nowes. Worked: `examples/case`.\n\n`only` lists the kinds a List holds a line for, so each kind stands in the Section whose job it is, once. The Act making a paper (`paper: \"<child>.<kind>\"`) is its line's button while it applies, the upload in the \u22EF.\n\n#### Templates\n\nA paper the business makes is an Act's `template`, kept in the record's files field `into` \u2014 or several at\nonce, `templates`: listed where the Act stands, each made one as its file, each not yet made a placeholder,\nthe reader ticking which to make. Worked: `examples/case`.\n\n#### Intake\n\nA paper the business is brought and reads is an Act's `intake`: the press takes the papers, an agent reads\nthem, and each is filed as the line of its kind in the child the record `expect`s, while `fills` writes what\nthey state into the record's fields \u2014 a single link as a row the agent finds among those the app reads \u2014 shown before it is saved. Worked: `examples/case`.\n\nAn agent Act \u2014 an `intake`, a `record`'s `fills` \u2014 spends a run on every press, so it is stated only where the\nowner asked for an agent to do that job; otherwise a paper brought is its `expect` line, and a person types what it states.\n\n#### Under\n\nA List with `under`: the documents that cover lines (an invoice over its fee lines) stand as headings\nover the lines they cover, and a line no document covers offers to make one. A document entered as it arrives over lines\nalready on file (a supplier bill) picks them with `pick` instead; choose one per document.\n\n### Calls\n\n#### Record act\n\nAn Act's `record`: the press records a call, visit or meeting \u2014 audio, transcript, screen where captured \u2014 and\nfiles it as a new row of the record's `timeline` child (`into`), stamped `at` and `by`; `fills` lets an agent fill\nthat row's fields from the transcript, where the owner asked. Worked: `examples/calls`.\n\n### Other apps\n\n#### Open act\n\nAn Act's `open`: the id of another app \u2014 usually a custom-code app drawing what the model cannot \u2014 that the press\nopens at this record. It is navigation only: no workflow of either app runs, and what the tool writes it writes\nthrough its own declared workflows. It stands alone with `label` and `when`, on one record of the app's own (an Act\nin a Record's section, or a compose `Act` piece naming it). The target must be an app of the workspace that reaches the\nrecord's table \u2014 a register over it, or custom code declaring no table or declaring it; anything else is refused at\nsave. A reader without `use` on the target meets that app's own \"cannot open\" state.\n\n## The visual bar\n\n- A person shows a face, a thing its picture, a file its preview, a plan its days on a time axis, a\n quantity a meter (`records.limits`) or a chart, a register its Readings under its title, and long text\n reads whole (a markdown Field is a prose block; `block: true` makes a plain long text one).\n- Every app states its tile's colour (the App's `color`), apart from the workspace's other apps where it can be,\n so the launcher tells the desks apart before their names are read.\n- Every category the job reads is coloured: each select option's `color`, and a `mark` where a brand or a\n glyph names it \u2014 on every option of a select read down a column (how it was paid, the channel, the mode).\n- A yes that needs attention (papers the customer still owes) is a `Check` of its List over a yes/no formula, so it\n reads in the warning tone on the row's line; every other yes reads in the success tone.\n- Someone is a face wherever they are read. A person is a member field and a party a row of a table stating\n `party`; where a select's options name people or organisations who are neither, every option's `mark` is\n its face (`{\"kind\": \"avatar\", \"name\": \"person\"}`), never a colour dot.\n- Every files field is drawn somewhere \u2014 a Section's Fields, a Field of the Record, the `image`, or where the\n Act making it stands.\n- The paperwork is the proof of the work, so every paper the job takes in and every document it issues has a\n place on the record, in the Section where that work happens: a files field per purpose (what arrived, what\n was issued to the customer, to the carrier, to the authority), each kept visible after the stage that made it,\n and a source \u2014 a screenshot, an email, a sheet \u2014 on the row whose figure came from it.\n- A screen with none of these is bland, and bland is a finding: text drawn where a treatment above exists\n is a change to the tree.\n\n## Look at it\n\nAfter an apply, the `screenshot_app` tool draws each app as its people see it \u2014 the register, and a record\nby its `path` \u2014 at a desk's width and a phone's, one screen a shot, naming the columns a table left off at\neach width: `screen` names which screen of a long record. From the CLI, `-o <dir>` saves the shots. Beside the shots, `verdict` is what a write of the app's stored tree would refuse it for now, beside its rows, and\nthe notes on it. Judge each shot against \xA7 The visual bar, change the tree with `nodes`, and write again. To go back,\nsend the tree you saved before the change as `body` with `app_id`, nulling an App prop stated since (`words`, `scope`,\n`period`, `icon`); the tables and rows stay as they are.\n";
|
package/docs/first_build.md
CHANGED
|
@@ -80,7 +80,7 @@ Every paper the owner sends is made, one template each. A slot is named after a
|
|
|
80
80
|
|
|
81
81
|
## 3. The app and its acts: `set_app`
|
|
82
82
|
|
|
83
|
-
**Which apps.**
|
|
83
|
+
**Which apps.** One per role's work, shaped to the requirement: the list that role opens, and everything about each row on its page whatever its rhythm. One HR person's app holds the people and, on each, their contracts, payslips and leave; a business where payroll and hiring are different people's work may want an app each. A related row's value on the list (the newest contract's end)? A lookup column. Work over many rows (a month's payroll)? An Act on the list's rows. Used only inside another job (the rates a quote is priced from)? A picker on its lines, price copied by `default_from`. Never an app per table.
|
|
84
84
|
|
|
85
85
|
**The view per kind of row**, chosen before writing the List (a plain register needs none):
|
|
86
86
|
|