@lotics/cli 0.317.0 → 0.318.1

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.
@@ -41,11 +41,11 @@ app has.
41
41
  | `<child>_by_<link>_days` | The <child> rows of the <entity> rows handed in whose "<day>" falls from one day to another. | `<entity>_ids`; `from`; `to` | a roster List: the List of its cells |
42
42
  | `<entity>_<link>_pick` | The <target> rows "<link>" may point at, narrowed by what the reader typed. | `search`?; `same_<link>` per `same` link | a written link a write rule narrows (`options_where`, `same`) |
43
43
  | `<child>_by_<link>_picks` | The <child> rows no <entity> holds through "<link>" yet, that one <entity> may pick, narrowed by what the reader typed. | `search`?; `same_<link>` per `same` link | a List with `pick`: it picks existing rows into the record |
44
- | `<reading key>` | <label>: how many <entity> rows there are. Each row carries `__count`. | the list's; `from`? / `to`? where the reading states `over`, or its `tab` is dated `in` a period; `to` where its `tab` is of rows `open` on a day | each Reading of the register List, called by its `key` (`register_reading_<n>`, n counted from 0, only for an app stored before trees) — but the work against its plan (Progress), which reads the list's own rows; none over Books |
45
- | `<reading key>` | <label>: how many <entity> rows there are. Each row carries `__count`. | `from`? / `to`? where the reading states `over` | each Reading of a dashboard, called by its `key` (`dashboard_reading_<n>` only for an app stored before trees) |
46
- | `<reading key>` | <label>: how many <child> rows there are. Each row carries `__count`. | `<entity>_id`; `same_<field>` where it is a list stating `same` | each Reading of the record and each List or funnel on it, called by its `key` (`block_reading_<n>` only for an app stored before trees) |
47
- | `<entity>_<key>` | <label>: how many <entity> rows there are. Each row carries `__count`. | the list's; `from`? / `to`? where the reading states `over` | each Reading of a log's List, called `<entity>_<key>` (`<entity>_register_reading_<n>` only for an app stored before trees) |
48
- | `<entity>_<key>` | <label>: how many <child> rows there are. Each row carries `__count`. | `<entity>_id`; `same_<field>` where it is a list stating `same` | each Reading of the record a row of a Book or of a log opens and each List or funnel on it, called `<entity>_<key>` by the Book's or the log's entity (`<entity>_block_reading_<n>` only for an app stored before trees) |
44
+ | `<reading key>` | <label>: how many <entity> rows there are. Each row carries `__count`. | the list's; `from`? / `to`? where the reading states `over`, or its `tab` is dated `in` a period; `to` where its `tab` is of rows `open` on a day | each Reading of the register List, called by its `key` — but the work against its plan (Progress), which reads the list's own rows; none over Books |
45
+ | `<reading key>` | <label>: how many <entity> rows there are. Each row carries `__count`. | `from`? / `to`? where the reading states `over` | each Reading of a dashboard, called by its `key` |
46
+ | `<reading key>` | <label>: how many <child> rows there are. Each row carries `__count`. | `<entity>_id`; `same_<field>` where it is a list stating `same` | each Reading of the record and each List or funnel on it, called by its `key` |
47
+ | `<entity>_<key>` | <label>: how many <entity> rows there are. Each row carries `__count`. | the list's; `from`? / `to`? where the reading states `over` | each Reading of a log's List, called `<entity>_<key>` |
48
+ | `<entity>_<key>` | <label>: how many <child> rows there are. Each row carries `__count`. | `<entity>_id`; `same_<field>` where it is a list stating `same` | each Reading of the record a row of a Book or of a log opens and each List or funnel on it, called `<entity>_<key>` by the Book's or the log's entity |
49
49
  | `<reading>_bound` | <label> / <bound>: the sum of "<bound>" over the <entity> rows. Each row carries `__count`, `__sum`. | its reading's | a Reading whose value `records.limits` bounds: the bound's sum, beside the Reading's own |
50
50
  | `<reading>_on_hand` | <label> / <on-hand entity>: how many <on-hand entity> rows there are, per "<field>". Each row carries `<field>`, `__count`. | none: every row of its entity its `where` keeps, whatever the place | a Reading with `on_hand` (a shortfall): the on-hand rows' groups, beside the Reading's own |
51
51
  | `<entity>_export_rows` | Every row of <entity> the reader may see, with every field "<template>" may print. | the list's | an Export of the register that fills a template |
@@ -281,7 +281,7 @@ Acts: every figure or list whose definition is the business's own (open orders,
281
281
  }
282
282
  ```
283
283
 
284
- It reads under the table's read scope and, on the app's own table, exactly the rows its list reads —
284
+ It reads under the app's `rows` (every row, where the app states none of the table) and, on the app's own table, exactly the rows its list reads —
285
285
  so never the table of an app that states Books, whose list reads several tables. A condition reading an
286
286
  optional param narrows nothing when the call leaves it out; no param compares a number read in each row's
287
287
  own unit or currency, or a date holding a span. `"reading"` in place of `"fields"` returns a count or a sum —
@@ -93,7 +93,8 @@ nothing; a refusal lists everything wrong in it at once.
93
93
 
94
94
  The App node is the app: its `name`, `description`, `icon` and `color` are props of it, and no other input of
95
95
  `set_app` states them. It names the tables and templates it uses by their aliases (an entity is a table's alias, a `template` a template's alias);
96
- `set_app` fills the `tables` and `templates` maps by id from them and `get_app` shows aliases (`lotics docs reference/app-node`). A call that creates
96
+ `set_app` fills the `tables` and `templates` maps by id from them and `get_app` shows aliases (`lotics docs reference/app-node`).
97
+ Who reads which rows is the app's `rows` (§ Who reads which rows, below). A call that creates
97
98
  is `{"body": <the App tree>}`; one that changes a part is `{"app_id": "app_…", "nodes": {"<address>": <patch>}}`,
98
99
  each address a node's path of keys (`get_app` prints every one), a patch a JSON merge patch of that node's props
99
100
  and `null` removing the node; the App's own props change through `body`. A call stating `base_version_id` — the
@@ -135,6 +136,29 @@ family and how it is called. A question the screen never asks is a Query node th
135
136
  (`lotics docs reference/query-node`), beside its Workflow nodes. Both are part of the design: `lotics docs
136
137
  agent_surface`, § Declaring more.
137
138
 
139
+ ### Who reads which rows
140
+
141
+ An app runs with its owner's access, and the people it is shared with need the app alone, never its tables. Who of them
142
+ reads which rows is the app's `rows`, one rule per table in the grammar of a table's `read_scope` (`lotics docs reference/read-scope`); it narrows
143
+ the app's screens, its queries and workflows called through it (`run_app_query`, `run_app_workflow`), its acts and its
144
+ workflow bodies alike, and a record id a caller passes is refused where the rule hides the row. State a rule only where
145
+ people differ; a table with none is read whole by everyone the app is shared with.
146
+
147
+ | The business says | The app states |
148
+ |---|---|
149
+ | Everyone on the team sees every order | nothing |
150
+ | Each rep sees only their own deals | `"rows": { "deal": { "any": [{ "field": "rep", "is": "self" }] } }` |
151
+ | Reps see their own deals; managers see all | `"rows": { "deal": { "any": [{ "field": "rep", "is": "self" }, { "member_of": "<managers group id>" }] } }` |
152
+ | Each branch manager sees their branch's orders | `"rows": { "order": { "any": [{ "through": ["branch"], "field": "manager", "is": "self" }] } }` |
153
+ | Each employee sees only their own payslips; HR sees all | two apps: one stating `"payslip": { "any": [{ "field": "employee", "is": "self" }] }`, one for HR stating none |
154
+ | Team leads see people, not salaries | an app that draws no salary field — what it does not show, no one reads through it |
155
+
156
+ A child read under a ruled row states its own rule, through its link to the parent; only a status history inherits.
157
+ Admins are not narrowed in an app; someone on a public link, signed in as no one, reads no row a `self` clause would
158
+ choose. An app never reads past its owner's own access. A table's own `read_scope` governs opening the table directly.
159
+ Working inside one row of a folder (`scope`) narrows the view, not who may read: a branch app shared with every manager
160
+ still states the branch rule.
161
+
138
162
  ## 5 — A custom-code app
139
163
 
140
164
  ```
package/docs/examples.md CHANGED
@@ -75,6 +75,14 @@ among them: the `design` reference. Every key: the `model` reference, at the pag
75
75
  "name": "Service visits",
76
76
  "icon": "calendar",
77
77
  "color": "emerald",
78
+ "rows": {
79
+ "customer": {"any": [{"member_of": "sales"}]},
80
+ "order": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
81
+ "order_line": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
82
+ "payment": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
83
+ "order_stage": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
84
+ "service_visit": {"any": [{"member_of": "sales"}, {"member_of": "stores"}, {"member_of": "field"}]}
85
+ },
78
86
  "children": [
79
87
  {
80
88
  "type": "List",
@@ -258,6 +266,14 @@ among them: the `design` reference. Every key: the `model` reference, at the pag
258
266
  "name": "Dispatch",
259
267
  "icon": "wrench",
260
268
  "color": "sky",
269
+ "rows": {
270
+ "customer": {"any": [{"member_of": "sales"}]},
271
+ "order": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
272
+ "order_line": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
273
+ "payment": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
274
+ "order_stage": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
275
+ "service_visit": {"any": [{"member_of": "sales"}, {"member_of": "stores"}, {"member_of": "field"}]}
276
+ },
261
277
  "children": [
262
278
  {
263
279
  "type": "List",
@@ -613,6 +629,13 @@ among them: the `design` reference. Every key: the `model` reference, at the pag
613
629
  "name": "Stock",
614
630
  "icon": "boxes",
615
631
  "color": "teal",
632
+ "rows": {
633
+ "customer": {"any": [{"member_of": "sales"}]},
634
+ "order": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
635
+ "order_line": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
636
+ "payment": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
637
+ "order_stage": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]}
638
+ },
616
639
  "scope": "warehouse",
617
640
  "children": [
618
641
  {
@@ -841,6 +864,16 @@ among them: the `design` reference. Every key: the `model` reference, at the pag
841
864
  "name": "Customers",
842
865
  "icon": "users",
843
866
  "color": "violet",
867
+ "rows": {
868
+ "customer": {"any": [{"member_of": "sales"}]},
869
+ "order": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
870
+ "order_line": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
871
+ "payment": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
872
+ "order_stage": {"any": [{"member_of": "sales"}, {"member_of": "stores"}]},
873
+ "touch": {"any": [{"member_of": "sales"}]},
874
+ "customer_paper": {"any": [{"member_of": "sales"}]},
875
+ "service_visit": {"any": [{"member_of": "sales"}, {"member_of": "stores"}, {"member_of": "field"}]}
876
+ },
844
877
  "children": [
845
878
  {
846
879
  "type": "List",
@@ -2,7 +2,7 @@
2
2
 
3
3
  A 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.
4
4
 
5
- Step 0, before the first write: ask the owner once, in one message, only what you cannot settle yourself. Usually: their papers and lists — a photo, a spreadsheet or a file is fine; they answer most questions (what they send and receive, customers, items, prices) — 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`).
5
+ Step 0, before the first write: ask the owner once, in one message, only what you cannot settle yourself. Usually: their papers and lists — a photo, a spreadsheet or a file is fine; they answer most questions (what they send and receive, customers, items, prices) — 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. Who reads which rows of an app is stated on the app (`rows`), only where people differ; absent, everyone the app is shared with sees every row, and members need the app shared with them, never table access. 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`).
6
6
 
7
7
  ## 1. Tables and their checks: `set_tables`
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.317.0",
3
+ "version": "0.318.1",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {