@lotics/cli 0.315.18 → 0.316.0

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/docs/design.md CHANGED
@@ -1,113 +1,77 @@
1
1
  # Designing an app
2
2
 
3
- Start here to build an app. This page is the method and the choice of treatment for each thing a job
4
- reads; every key it names is a key of `model.json`, and the page of the `model` reference stating a
5
- treatment's keys stands beside it in § Treatments. A treatment worked through in an app of a complete model
6
- is a page at `examples/<treatment>` — `lotics docs`, or the `docs` tool.
7
-
8
- A model states COMPOSITION — which fields, lists and acts each job's screen holds. How each piece looks is
9
- the platform's, one way per concept, and no key changes it. What the model leaves unnamed is never drawn,
10
- and a list the model leaves as a table is drawn as one, even where a calendar, a face or a picture would
11
- answer the reader at a glance. That is the most common failure of a model, so the apply refuses an app leaving
12
- out a treatment it requires — a register's readings, a record's sections and children, its status moves, a picture,
13
- rows in time, a template's act — until the model states it or declines it.
14
-
15
- ## The method
16
-
17
- 1. **People and jobs, before any table.** Name each person by the work they do, then each JOB in one
18
- sentence: who does it, what exists when it is done, the record it moves. The sentence is the app's
19
- `name` and `description`. A job is a desk a person works every day from its own list; one app per job,
20
- each one working register over one entity — an app per table, or one app holding every table, is no job's
21
- screen. What was brought into that register's rows or done to them — the files imported, the runs of a
22
- batch — is read beside it as a log of the same app (`logs`, at most three), never an app of its own.
23
- A job is the record it moves and the rhythm it moves at — the first list it opens on. What that record
24
- owns (a vehicle's papers, its services, its fines) is sections, blocks and acts of it, never an app;
25
- work over another record or at another rhythm (the week's dispatch runs, beside each vehicle's file) is
26
- another app, even when one person works both.
27
- A list worked only in passing on the way through another job — the rates a quote is priced from, the
28
- transfers its receipts are matched to, the statement sent from its records — is a block, a section or an
29
- act of that job's app, never an app of its own. A small business usually lands at four to six.
30
- 2. **The entities the jobs touch.** A status is a state the work sits in, six options or fewer: a touch
31
- ("called") is a row of a log, a money fact ("paid in full") a formula the figure shows. A quantity is a
32
- number with its `unit`, never words in a text field; a quantity of several kinds is child rows, one per
33
- kind, totalled at the foot of their block.
34
- 3. **How each row is recognised** — a `records` entry for every entity an app lists, opens or picks: its
35
- `title` (who or what it is, never a code — the code is a `subtitle`), up to two `subtitle` fields the job
36
- reads at first glance, its `image`, its `party`, its `status` and which options are `closed`, its
37
- `figure`, its `due` dates.
38
- 4. **Each app's composition** — the register's `columns` in the order the job reads them, as many as fit a
39
- desk (one that does not fit is drawn nowhere in the row; what is read only on the way deeper is the
40
- record's), the `filters` it narrows by every day (at most 3, each a field the rows show, or a formula yes/no of fields they show), its `layout`
41
- and `readings`; the record's `sections` in the order the work reaches them, each one's `fields`, `blocks`
42
- and `acts`; the `checks` that warn or refuse. A rule the business states — a field needed before an act,
43
- a quantity bounded, a picker narrowed, a duplicate reused — is the model's (an act's `requires`, a check's
44
- `blocks`, `write_rules`), and every write re-checks it; a check without `blocks` only warns.
45
- 5. **What a person would ask an agent in each app.** Write the asks down per app — every act, each list
46
- narrowed the way a person narrows it, one record with its lines and papers, every figure, data brought
47
- in — and beside each the query or workflow of the app that answers it (`agent_surface`). What the screen
48
- reads and does is generated. An ask no screen makes is declared: a read in `queries` (`model/queries`),
49
- a write in `workflows` (`model/workflows`). A figure an agent calls states its `alias`. An ask with
50
- nothing to answer it is a gap in the model, closed before the apply. An app is built to be run by an agent:
51
- every flow a person works can be driven end to end through its workflows, the person is asked only where
52
- their judgement or authority is needed, and the screens show what was done and let a person do or put back
53
- any step by hand.
54
- 6. **A treatment for every list and every entity** — the next section. Choose it from what the rows ARE,
55
- before writing any register.
56
- 7. **Plan, apply, look.** The `check_model` tool (`lotics model check` on the CLI) checks the
57
- whole model beside the workspace's rows and writes nothing — the patch adopting each app's decisions in
58
- `designs`, and the app's body as they leave it its `draft`; `apply_model` (`lotics model apply`) mints each app live. Then look at each app —
59
- § Look at it — and judge it against § The visual bar. A correction is a change to the model, applied again.
3
+ The 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`.
4
+
5
+ The common treatments are the chooser in `first_build`, § 3; every treatment is listed at `design/treatments`.
6
+
7
+ An app is a tree of components: a List of one table, the Record a row opens, its Sections, the Acts that move it. The
8
+ tree states COMPOSITION — which fields, lists and acts each job's screen holds. How each piece looks is the platform's,
9
+ one way per concept, and no prop changes it. A component bound to a table draws well with no props, because its defaults
10
+ come from the table's facts (`records`, the stage, the write rules): a node the tree leaves out draws that default — a
11
+ List's Readings band, the stage buttons, the picture, a Record's designed Sections, a head kept to its budget. State a
12
+ node to change its default, or `false` at it to turn it off (`"readings": false`, `"row": { "status": { "edit": false } }`,
13
+ `"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
14
+ answer the reader at a glance, and that is the most common failure of an app: the answer of `set_app` notes what the
15
+ rows call for that no default draws (rows in time, a template's Act, a timeline), and the tree states the ones the job
16
+ needs.
60
17
 
61
18
  ## Treatments
62
19
 
63
- A finding naming a section below is a treatment the model left out. A decided one refuses the apply, and the
64
- check gives each app it refuses a patch stating its decided treatments: merge the patches in the order the check gives
65
- them, or state why the app stays as it is in its `declines` (`model/declines`), under the key the finding names —
66
- then check again. A note refuses nothing.
20
+ A treatment named in a section below is one the answer of `set_app` notes when the rows call for it and neither the
21
+ tree nor a platform default draws it, naming the node that would state it. Nothing is written into the tree for the
22
+ author: state the ones the job needs. A note refuses nothing.
67
23
 
68
- | The rows are | The treatment | Its keys |
24
+ | The rows are | The treatment | Its props |
69
25
  |---|---|---|
70
- | People or organisations | `records.<entity>.party`, with a photo or logo as its `image` | `model/records` |
71
- | Things with a photo, a scan or a paper | `records.<entity>.image`; `layout: "cards"` where the picture is how a row is found | `model/records`, `model/cards` |
72
- | Read by their day | `layout: "calendar"`; on a record, an `agenda` block | `model/calendar`, `model/timeline-and-agenda` |
73
- | Booked on a resource | `layout: "lanes"` with `lanes`, and `write_rules` `no_overlap` | `model/lanes`, `model/write-rules` |
74
- | A plan of spans | `layout: "gantt"` with `gantt` | `model/gantt` |
75
- | Stood at a place — pallets, bins | `layout: "plan"`; places state `plan_place` | `model/plan` |
76
- | Who did what on which day | `layout: "roster"` with `roster` | `model/roster` |
77
- | A log of what happened | a `timeline` block | `model/timeline-and-agenda` |
78
- | Opened to be worked on | the record's `sections`, in the order the work reaches them — `at` where it moves in steps | `model/record-page` |
79
- | A record whose page is its own arrangement of pieces the app already states | `record.compose` | `model/composed-page` |
80
- | Rows that each belong to another row | a `rows` block on that row's record; a `list` block where read-only, stating `fields` for rows another app works | `model/blocks`, `model/list-block` |
81
- | Moving through a status | an act per move, `status.history`, and the register's `readings` | `model/acts`, `model/status`, `model/readings` |
82
- | Steps people drop out of between | a `funnel` block on the record whose child rows count them | `model/reading-blocks` |
83
- | A record's own numbers nobody keys, read at a glance | a `figures` block of two to eight tiles | `model/reading-blocks` |
84
- | 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/tabs` |
85
- | Papers a case gathers | a rows block with `expect`; the papers made, an act's `templates` | `model/blocks`, `model/papers-and-recordings` |
86
- | Rows that are each a place a record is sent | a rows block 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` |
87
- | Papers an agent reads, where the owner asked for one | an act's `intake` with `fills` | `model/papers-and-recordings` |
88
- | Calls and visits | an act's `record` | `model/papers-and-recordings` |
89
- | Changes a workflow proposes that a person approves row by row — 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/acts` |
90
- | Documents covering lines | a rows block with `under` | `model/blocks` |
91
- | Rows on file a record takes in | a rows block with `pick` | `model/blocks` |
92
- | One owner's question across jobs | a dashboard app | `model/dashboards` |
26
+ | People or organisations | `records.party`, with a photo or logo as its `image` | `model/records` |
27
+ | 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` |
28
+ | Read by their day | List `layout: "calendar"`; on a record, a child List with `layout: {"agenda": …}` | `model/calendar`, `model/layouts` |
29
+ | Booked on a resource | List `layout: {"lanes": {"by": …}}`, and the table's `no_overlap` | `model/lanes`, `model/write-rules` |
30
+ | A log of what happened | a child List with `layout: "timeline"` | `model/layouts` |
31
+ | Opened to be worked on | the Record's Sections, in the order the work reaches them — `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` |
32
+ | 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` |
33
+ | 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` |
34
+ | 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` |
35
+ | Papers a case gathers | a List with `expect`; the papers made, an Act's `templates` | `model/list-node`, `model/act-node` |
36
+ | A plan of spans | List `layout: {"gantt": {"start": …}}` | `model/gantt` |
37
+ | Stood at a place on a plan, flat or in 3D — pallets, bins | List `layout: {"plan": …}`; places state `plan_place` | `model/plan` |
38
+ | Checked item by item against a standard | a List with `checklist: {"photo": …, "clause": …, "failed": […], "passed": …}`: each row a mark (passed at `closed`, failed at a `failed` option, or by the yes/no `passed` names — 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` |
39
+ | A place before and after the work | a List with `compare: {"before": …, "after": …}`: two files fields read side by side, or two value fields read as one struck-then-new column | `model/list-node` |
40
+ | Spots on a drawing | `records.pin: {"on": …, "x": …, "y": …}` 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` |
41
+ | Photos that must be taken on site | `records.on_site: {"photos": […]}`: 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` |
42
+ | The real size of the thing (a carton, a piece of furniture, a pallet) | `records.size: {"across": …, "along": …, "up": …}`: three number fields in one length unit, read as one fact "400 × 300 × 250 mm" after the subtitle; a plan register draws each box at that size | `model/record-places-pins-and-cadence`, `model/plan` |
43
+ | 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` |
44
+ | Who did what on which day | List `layout: "roster"` with the List of its cells inside | `model/roster` |
45
+ | 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` — a link a member makes of the record, never a seat | `model/outside-node` |
46
+ | A record whose page is its own arrangement of pieces the app already states | Record `compose` | `model/composed-page` |
47
+ | Steps people drop out of between | a Reading with `steps` on the record whose child rows count them | `model/reading-node` |
48
+ | A record's own numbers nobody keys, read at a glance | consecutive Readings with no `entity` (two to eight tiles) | `model/reading-node` |
49
+ | 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` |
50
+ | Papers an agent reads, where the owner asked for one | an Act's `intake` with `fills` | `model/act-node` |
51
+ | Calls and visits | an Act's `record` | `model/act-node` |
52
+ | 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` |
53
+ | Changes a workflow proposes that a person approves row by row — 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` |
54
+ | Documents covering lines | a List with `under` | `model/list-node` |
55
+ | Rows on file a record takes in | a List with `pick` | `model/list-node` |
56
+ | One owner's question across jobs | an App of Tabs and Readings and no register | `model/app-node` |
93
57
 
94
58
  ### Faces and pictures
95
59
 
96
60
  #### Party
97
61
 
98
- `records.<entity>.party` is `"person"` where the rows are people (a contact, a patient, a driver) and
62
+ `records.party` is `"person"` where the rows are people (a contact, a patient, a driver) and
99
63
  `"organization"` where they are companies (a customer, a supplier, a carrier). Every surface drawing one of
100
64
  its rows — the register's row, a link's chip, a picker's option, the record's header — then draws a face:
101
65
  its `image` (a photo, a logo), else its initials. Without it, a person is drawn as a thing. A workspace
102
- member is a `select_member` field, and is drawn by their face already. Worked: `examples/party`.
66
+ member is a `member` field, and is drawn by their face already. Worked: `examples/party`.
103
67
 
104
68
  #### Image
105
69
 
106
- `records.<entity>.image` names the files field that pictures a row — a product's photo, a damage photo, a
70
+ `records.image` names the files field that pictures a row — a product's photo, a damage photo, a
107
71
  scan, a receipt. Every row of it then leads with its picture, or its paper's preview, wherever it is drawn;
108
- a link to it previews the file. Never a field an act keeps the paper it makes in (`into`). Register
72
+ a link to it previews the file. Never a field an Act keeps the paper it makes in (`into`). List
109
73
  `layout: "cards"` where the picture is how a row is found. A files field the record keeps as a folder of
110
- papers is a `files` block, read whole. Worked: `examples/cards`.
74
+ papers is drawn as a Field, read whole. Worked: `examples/cards`.
111
75
 
112
76
  #### A link opens its row
113
77
 
@@ -115,10 +79,10 @@ On a record, a link to one row opens its peek: a drawer on a desk, a sheet on a
115
79
  row's face — mark, name, status, line and its figure by its field's name — and nothing to edit. A link read in a
116
80
  row's cell or on its line opens from its name; a link the record keeps as a field rests as its picker, and
117
81
  its way out stands at the field's label where the row has somewhere to go. The peek's one act leaves for
118
- the app whose register is that entity's rows — the first, in the model's order, that the member
119
- can open — at the row's address, or goes to the row inside this app where the entity is the app's own,
120
- one of its books' or a log's, in that list — 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 asks opens
121
- no peek. The model states none of it: give the entity an app of its own and
82
+ the app whose register is that table's rows — the first, in the member's own order of apps, that the member
83
+ can open — at the row's address, or goes to the row inside this app where the table is the app's own,
84
+ one of its Books' or a log's, in that list — 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
85
+ asks opens no peek. No prop states it: give the table an app of its own and
122
86
  its links lead there. The app opened decides what the member reads of the row; a row it withholds
123
87
  lands them on its not-found.
124
88
 
@@ -126,157 +90,167 @@ lands them on its not-found.
126
90
 
127
91
  Rows that each stand on a day, or hold a run of days or hours, are read where they stand in time — never
128
92
  as two date columns. A row holds a span when its `records` line (title and subtitle) holds two dates, its
129
- `records.frees` names the day it ends, or `write_rules.<entity>.no_overlap` books it on a row its `by` links to (a
93
+ `records.frees` names the day it ends, or the table's `no_overlap` books it on a row its `by` links to (a
130
94
  chair, a machine). A `no_overlap` by a person or a value alone keeps one row at a time each — a member's periods in
131
95
  turn — read by what they hold.
132
96
 
133
97
  #### Calendar
134
98
 
135
- `register.layout: "calendar"`: the rows by their day — a month, a week, a day or a list; an entry at its
99
+ `layout: "calendar"`: the rows by their day — a month, a week, a day or a list; an entry at its
136
100
  hour where its date holds one, over its span where its line holds a second date. An entry pressed opens a card on
137
- it: the row, Delete, its acts, Reschedule (the day alone moves; its hour and its length stay) and Open. Worked:
101
+ it: the row, Delete, its Acts, Reschedule (the day alone moves; its hour and its length stay) and Open. Worked:
138
102
  `examples/calendar`.
139
103
 
140
104
  #### Lanes
141
105
 
142
- `register.layout: "lanes"` with `lanes` naming a one-row link whose target's rows are the lanes (a chair, a
106
+ `layout: {"lanes": {"by": …}}` with `by` naming a one-row link whose target's rows are the lanes (a chair, a
143
107
  machine, a room), each drawn even when empty: every row a block from the first date on its line to the
144
108
  second, and a free stretch adds a row there. `loads` reads what a lane carries against what it holds. A
145
109
  `no_overlap` `by` the same link refuses two rows on one resource at once. Worked: `examples/lanes`.
146
110
 
147
111
  #### Gantt
148
112
 
149
- `register.layout: "gantt"` with `gantt.start` (and `end`, else the entity's first `due`): one bar a row over
150
- its run of days — a shipment, a hire, a task of a project — read against today. `gantt.milestones` marks
113
+ `layout: {"gantt": {"start": …}}` (and `end`, else the table's first `due`): one bar a row over
114
+ its run of days — a shipment, a hire, a task of a project — read against today. `milestones` marks
151
115
  dates on the bar, `planned` the plan under it, `progress` how far it is done, `after` the rows it waits on;
152
- the register's `group` is its lanes. Worked: `examples/gantt`.
116
+ the List's `group` is its lanes. Worked: `examples/gantt`.
153
117
 
154
118
  #### Roster
155
119
 
156
- `register.layout: "roster"` with `roster` naming the child whose rows fill the days — one link back to the
157
- register's entity and a date on its line: what each row did on each day (shifts, runs, attendance). With
158
- `expect` (and `of`) the columns are the options of a select instead of days: which papers each row holds.
120
+ `layout: "roster"` with a List inside it over the child whose rows fill the days — one link back to the
121
+ register's table and a date on its line: what each row did on each day (shifts, runs, attendance). With
122
+ `expect` (and `of`) on the cells the columns are the options of a select instead of days: which papers each row holds.
159
123
  Worked: `examples/roster`.
160
124
 
161
125
  #### Agenda
162
126
 
163
- An `agenda` block on a record: a child's planned entries by day, soonest first, today marked, each with its
127
+ A child List with `layout: {"agenda": {…}}` on a record: a child's planned entries by day, soonest first, today marked, each with its
164
128
  picture; within a day by the hour, or by a single select of the part of the day. `start` counts Day 1 from a
165
129
  date of the record (a trip, a course).
166
130
 
167
131
  #### Timeline
168
132
 
169
- A `timeline` block on a record: a child read as a log of dated entries, newest first, with a composer — the
133
+ A child List with `layout: "timeline"` on a record: a child read as a log of dated entries, newest first, with a composer — the
170
134
  calls, visits and notes about the record, each with who and when, and its files where the child's `image`
171
- names them. Entries planned ahead are an `agenda`; the status's moves are its history. Worked:
135
+ names them. Entries planned ahead are an agenda; the status's moves are its history. Worked:
172
136
  `examples/party`.
173
137
 
174
138
  ### Moves and numbers
175
139
 
176
140
  #### Moves
177
141
 
178
- Rows a status stages towards its `closed` options move by acts: each moves one option on to the next (`when` the
179
- status holds it, `set` to the next), at the foot of the section whose work it ends. A status with nothing closed is a
142
+ Rows a status stages towards its `closed` options move by Acts: each moves one option on to the next (`when` the
143
+ status holds it, `set` to the next), at the foot of the Section whose work it ends. A status with nothing closed is a
180
144
  condition a person sets. A value decided as a row reaches an outcome — why it was lost, refused, voided — is that
181
- act's `asks`, never a field the reader must remember to fill. An act reaching past the workspace — sending an invoice
145
+ Act's `asks`, never a field the reader must remember to fill. An Act reaching past the workspace — sending an invoice
182
146
  to a service, a letter by mail — runs its `workflow`, and what the workflow keeps from the answer (an invoice's id,
183
- its link) is named in `writes`, the act's as what it sets is. A stage the work only steps aside into from one step
184
- and leaves only back to it — a pause, a repair — is a detour: its option and its section stand before that step,
185
- entered by its own act; listed after, it reads as the work's next step.
147
+ its link) is named in the Act's `set`. A stage the work only steps aside into from one step
148
+ and leaves only back to it — a pause, a repair — is a detour: its option and its Section stand before that step,
149
+ entered by its own Act; listed after, it reads as the work's next step.
186
150
 
187
151
  #### Stages
188
152
 
189
153
  Name each job's work plan by its shape: a status a person sets; a pipeline, where who holds the row and for how long
190
- matter; or tasks, where only how many are done counts (`records.<entity>.task`). A pipeline states each stage once in
191
- `records.<entity>.status.stages` — its `owner` (a member field, a link's member field, or a `role`) and its target
192
- `within` days — and the server holds it: only the owner's acts move the row on, and a level an amount skips stands
193
- named with why. Late rows, rows due soon, a warning the line draws and rows waiting on the reader stand first in a
194
- register; a register whose job is a strict order, a first-come queue, states `register.attention: false`.
195
-
196
- #### History
197
-
198
- `records.<entity>.status.history` names a child the app's own writes append one row to per status move: its
199
- link to the row, the new status, the moment and who. The rail reads it. A record's activity shows every move as a change, so `record.history: true` adds
200
- nothing — and beside `record.activity: false`, which shows them nowhere, it is
201
- refused. Worked: `examples/approval`.
154
+ matter; or tasks, where only how many are done counts (`records.task`). A pipeline states each stage once in
155
+ `records.status.stages` — its `owner` (a member field, a link's member field, or `{ "group": <id> }`, a member group) and its target
156
+ `within` days — 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
157
+ named with why. A move forward passes each owned stage between that binds the row, each its owner's. A stage's `when`
158
+ names 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
159
+ and as the write leaves it; with none, it binds every row. Expense claims over a limit wait for the finance group while
160
+ smaller ones go straight to payment: `"finance_review": { "owner": { "group": <id> }, "when": { "amount": { "gt": 5000000 } } }`.
161
+ A row at a stage leaves it by its owner whatever its `when`; `dropped` names the closed options
162
+ that 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
163
+ register; a register whose job is a strict order, a first-come queue, states `attention: false`.
164
+
165
+ #### Stage dates and history
166
+
167
+ Who 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
168
+ to the business — reported, filtered, printed — it is a date on the row that `records.status.dates` stamps as the row
169
+ enters the option, whoever writes: the one way a status is timed, and the moment a stage's `within` runs from (each stage, and
170
+ each 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
171
+ day 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
172
+ status move (its link to the row, the new status, the moment and who) for a business that reports on every move: moves
173
+ per person, or time in a stage a row re-enters. An event with facts of its own — a reason, an amount, a signature — is
174
+ its 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
175
+ stage is signed once per person, read off who moved the row.
202
176
 
203
177
  #### Readings
204
178
 
205
- `register.readings` stand above the rows and read the rows in view. Lead with the job's headline number —
206
- its exception as a formula's yes/no in a `metric`'s `where` (what is late, what is short), never a stage the
207
- status chips already count — then at most the mix (`breakdown`) and the movement (`trend`), varied by job.
208
- A `pivot` reads on a dashboard. A dashboard app (`dashboard`) answers one owner's question across jobs, each
209
- reading windowed by the period the reader picks, and a `list` reading hands over the rows to act on. A
210
- dashboard of many readings states `tabs` (2 to 5) and each reading the `tab` it stands under, so one sheet draws at a time. A
211
- record's `metric`, `breakdown` and `trend` blocks read its own child rows. Sums of each row's child rows are a tab's `totals`. Worked: `examples/approval`, `examples/dashboard`.
179
+ A List's Readings stand above the rows and read the rows in view. Lead with the job's headline number —
180
+ its exception as a formula's yes/no in a Reading's `where` (what is short), never a stage the
181
+ status chips already count, nor the overdue rows the register's summary line counts from `records.due` — 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.
182
+ A grid (`by` and `across`) reads on a dashboard. A dashboard — an App of Readings and no register — answers one owner's question across jobs, each
183
+ Reading windowed by the period the reader picks, and a List hands over the rows to act on. A
184
+ dashboard of many Readings states Tabs (2 to 5) and each Reading the `tab` it stands under, so one sheet draws at a time. A
185
+ Record's Readings read its own child rows. Sums of each row's child rows are a Tab's `totals`. Worked: `examples/approval`, `examples/dashboard`.
212
186
 
213
187
  #### Funnel
214
188
 
215
- A `funnel` block reads a record's child rows as steps, each a number column summed over them — a week's channels
189
+ A Reading with `steps` reads a record's child rows as steps, each a number column summed over them — a week's channels
216
190
  from impressions to bookings, an order's lines from ordered to shipped. It states how much of the first step
217
- each later one keeps, the share lost between two neighbours, and the last over the first. State two to eight
218
- `steps` in one unit; where the path turns — reach, then conversion — state two blocks one straight after the
191
+ each later one keeps, the share lost between two neighbours, and the last over the first. State two to six
192
+ `steps` in one unit; where the path turns — reach, then conversion — state two Readings one straight after the
219
193
  other, so the second's bars are read over its own first step: they draw on one grid, every bar starting at one
220
194
  edge 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.
221
195
 
222
196
  #### Priced lines
223
197
 
224
- A quote's items, an estimate's works, a repair sheet's damages are lines priced off a book: an entity whose
198
+ A quote's items, an estimate's works, a repair sheet's damages are lines priced off a book: a table whose
225
199
  `records` names each item, puts the code people type on its line and its price as its `figure`. The line links to
226
- it, asks the link and its quantities in its block's `create`, and copies the price (`default_from`), so the quote
200
+ it, asks the link and its quantities in its List's `create`, and copies the price (`default_from`), so the quote
227
201
  keeps what was quoted when the book moves. A line's amount is a formula, the record's total a rollup over the rows
228
- the block shows — what is not paid for is a second rollup, never a filter on the first, so each group subtotals.
229
- Group the lines by the parts the record is quoted in (a container, a room, a leg), a field the line asks. The book
230
- is its keeper's desk, an app of its own.
202
+ the List shows — what is not paid for is a second rollup, never a filter on the first, so each group subtotals.
203
+ Group 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
204
+ an app (`first_build` § 3).
231
205
 
232
206
  ### The record
233
207
 
234
208
  #### Sections
235
209
 
236
- `record.sections`, in the order the work reaches them: the fields people write, a `files` block per paper the record
237
- keeps, a block per child, the acts at each one's foot. A register laid out in time (calendar, lanes, roster) opens the
238
- default record: every field a person writes. `record.compose` is not for a record a standard section order would
239
- do, and never for behaviour, which belongs in an act.
210
+ The Record's Sections, in the order the work reaches them: the fields people write, a Field per paper the record
211
+ keeps, 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
212
+ default record: every field a person writes. `compose` is not for a record a standard Section order would
213
+ do, and never for behaviour, which belongs in an Act.
240
214
 
241
215
  #### Children
242
216
 
243
- Rows that each belong to one row of another entity (a one-row link to it) are drawn under that row: a `rows` block on
244
- its record, a `timeline` where they are a log, an `agenda` where they are planned on a day and within it, a roster of
245
- them, or lanes of them by that link. Which children a record lists is the author's — one of them at least. A block
217
+ Rows that each belong to one row of another table (a one-row link to it) are drawn under that row: a List on
218
+ its Record, `layout: "timeline"` where they are a log, `layout: {"agenda": …}` where they are planned on a day and within it, a roster of
219
+ them, or lanes of them by that link. Which children a record lists is the author's — one of them at least. A List
246
220
  listing only some of them narrows by its `where`: a select (one leg's fees), or a yes/no — a formula's for what is
247
- still owed — on a block that adds no rows. Rows on
221
+ still owed — on a List that adds no rows. Rows on
248
222
  file before the row they come to belong to — a job's cost lines before the supplier bill over them — are picked into
249
- it: a rows block with `pick`, often with `create: false` and `remove: false`.
223
+ it: a List with `pick`, often with no `create` and no `remove`.
250
224
 
251
225
  ### Papers
252
226
 
253
227
  #### Expect
254
228
 
255
- A rows block with `expect` naming the child's title — a single select of kinds, or a one-row link to a
229
+ A List with `expect` naming the child's title — a single select of kinds, or a one-row link to a
256
230
  catalog: one line per kind, the ones not yet filled standing empty, filled in place. `of` narrows the kinds
257
231
  to the record's own multi-select of those it needs. The papers a case gathers, the fees a record knows it
258
232
  owes. Worked: `examples/case`.
259
233
 
260
- `only` lists the kinds a block 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 ⋯.
234
+ `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 ⋯.
261
235
 
262
236
  #### Templates
263
237
 
264
- A paper the business makes is an act's `template`, kept in the record's files field `into` — or several at
265
- once, `templates`: listed where the act stands, each made one as its file, each not yet made a placeholder,
238
+ A paper the business makes is an Act's `template`, kept in the record's files field `into` — or several at
239
+ once, `templates`: listed where the Act stands, each made one as its file, each not yet made a placeholder,
266
240
  the reader ticking which to make. Worked: `examples/case`.
267
241
 
268
242
  #### Intake
269
243
 
270
- A paper the business is brought and reads is an act's `intake`: the press takes the papers, an agent reads
244
+ A paper the business is brought and reads is an Act's `intake`: the press takes the papers, an agent reads
271
245
  them, and each is filed as the line of its kind in the child the record `expect`s, while `fills` writes what
272
246
  they state into the record's fields — a single link as a row the agent finds among those the app reads — shown before it is saved. Worked: `examples/case`.
273
247
 
274
- An agent act — an `intake`, a `record`'s `fills` — spends a run on every press, so it is stated only where the
248
+ An agent Act — an `intake`, a `record`'s `fills` — spends a run on every press, so it is stated only where the
275
249
  owner asked for an agent to do that job; otherwise a paper brought is its `expect` line, and a person types what it states.
276
250
 
277
251
  #### Under
278
252
 
279
- A rows block with `under`: the documents that cover lines (an invoice over its fee lines) stand as headings
253
+ A List with `under`: the documents that cover lines (an invoice over its fee lines) stand as headings
280
254
  over the lines they cover, and a line no document covers offers to make one. A document entered as it arrives over lines
281
255
  already on file (a supplier bill) picks them with `pick` instead; choose one per document.
282
256
 
@@ -284,37 +258,49 @@ already on file (a supplier bill) picks them with `pick` instead; choose one per
284
258
 
285
259
  #### Record act
286
260
 
287
- An act's `record`: the press records a call, visit or meeting — audio, transcript, screen where captured — and
261
+ An Act's `record`: the press records a call, visit or meeting — audio, transcript, screen where captured — and
288
262
  files it as a new row of the record's `timeline` child (`into`), stamped `at` and `by`; `fills` lets an agent fill
289
263
  that row's fields from the transcript, where the owner asked. Worked: `examples/calls`.
290
264
 
265
+ ### Other apps
266
+
267
+ #### Open act
268
+
269
+ An Act's `open`: the id of another app — usually a custom-code app drawing what the model cannot — that the press
270
+ opens at this record. It is navigation only: no workflow of either app runs, and what the tool writes it writes
271
+ through its own declared workflows. It stands alone with `label` and `when`, on one record of the app's own (an Act
272
+ in a Record's section, or a compose `Act` piece naming it). The target must be an app of the workspace that reaches the
273
+ record's table — a register over it, or custom code declaring no table or declaring it; anything else is refused at
274
+ save. A reader without `use` on the target meets that app's own "cannot open" state.
275
+
291
276
  ## The visual bar
292
277
 
293
278
  - A person shows a face, a thing its picture, a file its preview, a plan its days on a time axis, a
294
- quantity a meter (`records.limits`) or a chart, a register its `readings` under its title, and long text
295
- reads whole (a `text` block).
296
- - Every app states its tile's colour (`theme.color`), apart from the workspace's other apps where it can be,
279
+ quantity a meter (`records.limits`) or a chart, a register its Readings under its title, and long text
280
+ reads whole (a markdown Field is a prose block; `block: true` makes a plain long text one).
281
+ - Every app states its tile's colour (the App's `color`), apart from the workspace's other apps where it can be,
297
282
  so the launcher tells the desks apart before their names are read.
298
283
  - Every category the job reads is coloured: each select option's `color`, and a `mark` where a brand or a
299
284
  glyph names it — on every option of a select read down a column (how it was paid, the channel, the mode).
300
- - Someone is a face wherever they are read. A person is a member field and a party a row of an entity stating
285
+ - A yes that needs attention (papers the customer still owes) is a `Check` of its List over a yes/no formula, so it
286
+ reads in the warning tone on the row's line; every other yes reads in the success tone.
287
+ - Someone is a face wherever they are read. A person is a member field and a party a row of a table stating
301
288
  `party`; where a select's options name people or organisations who are neither, every option's `mark` is
302
289
  its face (`{"kind": "avatar", "name": "person"}`), never a colour dot.
303
- - Every files field is drawn somewhere — a section's `fields`, a `files` block, the `image`, or where the
304
- act making it stands.
290
+ - Every files field is drawn somewhere — a Section's Fields, a Field of the Record, the `image`, or where the
291
+ Act making it stands.
305
292
  - The paperwork is the proof of the work, so every paper the job takes in and every document it issues has a
306
- place on the record, in the section where that work happens: a files field per purpose (what arrived, what
293
+ place on the record, in the Section where that work happens: a files field per purpose (what arrived, what
307
294
  was issued to the customer, to the carrier, to the authority), each kept visible after the stage that made it,
308
295
  and a source — a screenshot, an email, a sheet — on the row whose figure came from it.
309
296
  - A screen with none of these is bland, and bland is a finding: text drawn where a treatment above exists
310
- is a change to the model.
297
+ is a change to the tree.
311
298
 
312
299
  ## Look at it
313
300
 
314
301
  After an apply, the `screenshot_app` tool draws each app as its people see it — the register, and a record
315
302
  by its `path` — at a desk's width and a phone's, one screen a shot, naming the columns a table left off at
316
- each width: `screen` names which screen of a long record. From the CLI, `-o <dir>` saves the shots. Beside the shots, its `verdict` is what a plan of the workspace's
317
- model finds of the app now, beside its rows: what the next apply would refuse it for and the notes on it, the
318
- `patch` adopting the decisions among them, and the app's body as the patch leaves it (`draft`). Judge each shot
319
- against § The visual bar, change the model, and apply again. The `rollback_app` tool returns an app to an earlier
320
- version; the tables and rows stay as they are.
303
+ each 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
304
+ the notes on it. Judge each shot against § The visual bar, change the tree with `nodes`, and write again. To go back,
305
+ send the tree you saved before the change as `body` with `app_id`, nulling an App prop stated since (`words`, `scope`,
306
+ `period`, `icon`); the tables and rows stay as they are.