@lotics/cli 0.245.0 → 0.247.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.
@@ -37,6 +37,13 @@ export interface AppQueryBody {
37
37
  keyset?: boolean;
38
38
  /** The previous page's `next_cursor`, in keyset mode. */
39
39
  cursor?: string;
40
+ /** Answer the groups of the filtered set in place of its rows: by up to two output columns and the day of an
41
+ * output date, each group's `__count` and `__sum` of one output number. */
42
+ aggregate?: {
43
+ by?: string[];
44
+ day?: string;
45
+ sum?: string;
46
+ };
40
47
  }
41
48
  /** Result of the app query RPC. `total` answers a `count` read; `truncated` and
42
49
  * `next_cursor` a rows read — `next_cursor` in keyset mode only, null on the
@@ -370,8 +377,6 @@ export interface ScaffoldWorkspaceRequest {
370
377
  }>>;
371
378
  /** Bind an entity whose label already names a table here. Absent, a colliding label is refused. */
372
379
  adopt?: boolean;
373
- /** The account each connection alias pushes through, by `cac_` id — where more than one of its provider is usable. */
374
- connections?: Record<string, string>;
375
380
  }
376
381
  export interface ScaffoldWorkspaceResult {
377
382
  /** Every entity the model declares, in contract order. */
@@ -423,48 +428,6 @@ export interface ScaffoldWorkspaceResult {
423
428
  * whose rule nobody named is the one a reader has to be told about.
424
429
  */
425
430
  carried_rules?: string[];
426
- /**
427
- * Every table automation the model declares that this run wrote, with the
428
- * table workflow it is: created by this run, or rewritten in place — found
429
- * through the binding (`id`) or by its label on its table — or `restored`
430
- * from archived. `enabled` is whether it fires — a rewrite keeps the owner's
431
- * switch — and is absent from an instance too old to say. Absent from an
432
- * instance too old to provision one, which also refuses a model that declares
433
- * any.
434
- */
435
- automations?: Array<{
436
- alias: string;
437
- entity: string;
438
- table_workflow_id: string;
439
- bound_by: "created" | "id" | "label" | "restored";
440
- enabled?: boolean;
441
- }>;
442
- /**
443
- * Automations this run neither created nor rewrote: deployed app workflows
444
- * write the table each keeps (`keeps`, an entity alias) themselves, and both
445
- * writing it would land every row twice. `live` is the table workflow already
446
- * writing it beside them — twice NOW — else null. Absent from an instance too
447
- * old to hold one back.
448
- */
449
- held_automations?: Array<{
450
- alias: string;
451
- entity: string;
452
- keeps: string;
453
- writers: AppTableWriter[];
454
- live: string | null;
455
- }>;
456
- /**
457
- * The account each connection the model declares pushes through: named by the
458
- * caller (`chosen`), kept from the binding this workspace held (`id`), or the
459
- * only account of its provider the applier can use (`only`). Absent from an
460
- * instance too old to bind one, which also refuses a model that declares any.
461
- */
462
- connections?: Array<{
463
- alias: string;
464
- connected_account_id: string;
465
- provider: string;
466
- bound_by: "chosen" | "id" | "only";
467
- }>;
468
431
  /**
469
432
  * Options of an ADOPTED field that kept a mark other than the model's, by
470
433
  * binding key — every surface draws the live one. Absent from an instance too
@@ -514,22 +477,11 @@ export interface BoundTarget {
514
477
  export interface BoundField extends BoundTarget {
515
478
  options: Record<string, BoundTarget>;
516
479
  }
517
- /** One app workflow alias whose deployed body writes a table's rows. */
518
- export interface AppTableWriter {
519
- app_id: string;
520
- app_name: string;
521
- workflow_alias: string;
522
- }
523
480
  /** One entity of the model, as this workspace holds it. */
524
481
  export interface BoundEntity {
525
482
  table: string;
526
483
  live_label: string | null;
527
484
  fields: Record<string, BoundField>;
528
- /**
529
- * The deployed app workflows that write this table — only on an entity the
530
- * read named in `app_writers`, and absent from an instance too old to say.
531
- */
532
- app_writers?: AppTableWriter[];
533
485
  }
534
486
  /**
535
487
  * WHAT EACH ALIAS OF A WORKSPACE MODEL BECAME IN ONE WORKSPACE.
@@ -544,16 +496,12 @@ export interface ModelBinding {
544
496
  entities: Record<string, BoundEntity>;
545
497
  templates: Record<string, BoundTarget>;
546
498
  roles: Record<string, BoundTarget>;
547
- /** Automation alias → the table workflow it became. Absent from an instance too old to bind one. */
548
- automations?: Record<string, BoundTarget>;
549
499
  /** `<entity-alias>:<row-ref>` → the record that first row became. */
550
500
  rows: Record<string, string>;
551
501
  /** The path a row attached a document by → the file it was uploaded as. */
552
502
  documents: Record<string, string>;
553
- /** A plan's app alias → the app created for it here. Absent from an instance too old to bind one. */
503
+ /** A model's app alias → the app created for it here. Absent from an instance too old to bind one. */
554
504
  apps?: Record<string, BoundTarget>;
555
- /** A connection alias → the connected account an apply chose for it. Absent from an instance too old to bind one. */
556
- connections?: Record<string, BoundTarget>;
557
505
  }
558
506
  /**
559
507
  * A workspace read BACK as a model — the inverse of the scaffold above.
@@ -884,9 +832,7 @@ export declare class LoticsClient {
884
832
  * additive verb creates a second table over. Admin-only, like the scaffold it
885
833
  * is the memory of.
886
834
  */
887
- getModelBinding(opts?: {
888
- app_writers?: readonly string[];
889
- }): Promise<ModelBinding>;
835
+ getModelBinding(): Promise<ModelBinding>;
890
836
  /**
891
837
  * Forget what one alias is bound to here — the escape from a binding whose
892
838
  * target has been deleted. Forgets everything addressed under it too: an
@@ -396,10 +396,8 @@ var LoticsClient = class {
396
396
  * additive verb creates a second table over. Admin-only, like the scaffold it
397
397
  * is the memory of.
398
398
  */
399
- async getModelBinding(opts = {}) {
400
- const writers = opts.app_writers ?? [];
401
- const qs = writers.length === 0 ? "" : `?${new URLSearchParams({ app_writers: writers.join(",") }).toString()}`;
402
- return this.request("GET", `/v1/workspaces/model/binding${qs}`);
399
+ async getModelBinding() {
400
+ return this.request("GET", "/v1/workspaces/model/binding");
403
401
  }
404
402
  /**
405
403
  * Forget what one alias is bound to here — the escape from a binding whose
@@ -13,43 +13,49 @@ using one to find out whether something works is the slowest possible way to lea
13
13
  ## 1 — Scaffold, or pull what exists
14
14
 
15
15
  ```
16
- lotics app preview model.json#<app> --shots shots/ # the plan rendered over its rows: no workspace
16
+ lotics app preview model.json#<app> --shots shots/ # the model's app rendered: no workspace
17
17
  lotics app create "<name>" # new: a real Vite + React + TS project, deps installed
18
- lotics app create "<name>" --from model.json#<app> # new, from a checked plan: app.json, rendered
18
+ lotics app create "<name>" --from model.json#<app> # new, from a checked model: app.json, rendered
19
19
  lotics app create "<name>" --api # new, no screens: its declarations are the whole surface
20
- cd <dir> && lotics app regenerate # existing + generated: rewrite its spec from the plan
20
+ cd <dir> && lotics app regenerate # existing + generated: recompile it from the model
21
21
  cd <dir> && lotics app pull <app_id> # existing: refresh to the latest first, then read its README
22
- lotics workspace build model.json # the whole plan: check, apply, then every app in it
22
+ lotics workspace build model.json # the whole model: check, apply, then every app in it
23
23
  ```
24
24
 
25
25
  `lotics workspace build` is the create and regenerate lines above in one, for a model that
26
26
  declares several apps: `scaffold check`, `scaffold apply` where this workspace differs from the
27
- model or the model declares a table automation, then per app `create --from` into
28
- `<dir-of-model>/<alias-with-dashes>` when that directory does not exist and `regenerate` there when
29
- it does, then `app check` — and `app deploy` under `--deploy`. Each app is built after every sibling
30
- it names that the run creates, since a hand-off is written off the app its alias became; otherwise
31
- in the model's order. Two apps that name each other cannot both go second, so the one built first
32
- is built again once the other exists, and a hand-off binds on the first run. An app that conflicts or checks
33
- red is named and the rest still run, because the one you are going to fix is not a reason to leave
34
- the others in a state nobody knows. `--dry-run` writes nothing and says which apps it would create
35
- and which it would regenerate.
36
-
37
- **Look at the plan before anything exists.** `lotics app preview <model.json>#<app>` binds the app
38
- against the model's own ids, answers every read from the model's `rows`, renders every screen and
39
- record at 1280 and 375, and runs the probes `app check --screens` runs — no workspace, no
40
- credential, nothing created. `--shots <dir>` writes what it measured as PNGs. What a clause of the
41
- plan draws is `lotics docs clauses`; the preview is how you see it drawn over your own rows.
27
+ model, then per app `create --from` into `<dir-of-model>/<alias-with-dashes>` when that directory
28
+ does not exist and `regenerate` there when it does, then `app check` — and `app deploy` under
29
+ `--deploy`. An app that conflicts or checks red is named and the rest still run, because the one
30
+ you are going to fix is not a reason to leave the others in a state nobody knows. `--dry-run`
31
+ writes nothing and says which apps it would create and which it would regenerate.
32
+
33
+ **An app is stated, not drawn.** In `model.json`, `records` says how a row of each entity is
34
+ recognised — its title, the line under it, its picture, its status, its figure, its deadlines — and
35
+ each entry of `apps` is one register over one entity and the record its rows open: the register's
36
+ columns and filters, the record's fact groups and blocks, the acts and the checks that guard them.
37
+ That is the whole vocabulary (`lotics docs model/apps`). How each piece looks is the runtime's, one
38
+ way per concept, and no key changes it. `lotics scaffold check` prints each app as its reader will
39
+ see it, with every value a default supplied marked `(default)` — review the app there, before
40
+ anything exists.
41
+
42
+ **Look at it before anything exists.** `lotics app preview <model.json>#<app>` compiles the app
43
+ against the model's own ids, answers every read from the model's `rows` (a few synthesized for an
44
+ entity that states none), renders the register, the record in its door and the add dialog at 1280
45
+ and 375, and runs the probes `app check --screens` runs — no workspace, no credential, nothing
46
+ created. `--shots <dir>` writes what it measured as PNGs.
42
47
 
43
48
  `--from` takes the model file `lotics scaffold check` approved and the app in it, and writes
44
- **`app.json`** — the bound plan: its one register with the shape and slots, the record each row
45
- opens, the create panels and the acts, addressing the live `tbl_`/`fld_`/`opt_` ids its entities
46
- became — beside a five-line `src/main.tsx` that mounts `@lotics/app-runtime` over it, one
47
- `src/workflows/<alias>.ts` per write and an empty `src/components/index.ts`. The runtime draws the
49
+ **`app.json`** — the app compiled against the live `tbl_`/`fld_`/`opt_` ids its entities became —
50
+ beside a `src/main.tsx` that mounts `@lotics/app-runtime` over it, one `src/workflows/<alias>.ts`
51
+ per write and an empty `src/components/index.ts`. Every write — a create, an edit, a remove, each
52
+ act — is a generated workflow that re-checks on the server what the model states: required fields,
53
+ `write_rules`, an act's `when` and `requires`, and the checks that block it. The runtime draws the
48
54
  spec; there is no screen source to edit (`node_modules/@lotics/app-runtime/AGENTS.md`). The tables
49
55
  have to exist (`lotics scaffold apply` first); a table or field the workspace lacks is refused by
50
- name. The app is recorded against its plan alias, so a second `--from` for the same alias is
51
- refused and a sibling's hand-off to it opens this app. The project's `README.md` is the plan in
52
- prose: the app, its register and record, and the tables behind them.
56
+ name. The app is recorded against its model alias, so a second `--from` for the same alias is
57
+ refused. The project's `README.md` is the app in prose: its register, its record and the tables
58
+ behind them.
53
59
 
54
60
  `--api` scaffolds the app something OUTSIDE Lotics calls (§ 9): the manifest, `src/workflows/`, a
55
61
  CI job and the two briefs, with no `index.html`, no `src/App.tsx`, no Vite config and no kit. Its
@@ -67,15 +73,15 @@ A pull writes more than source: one `src/workflows/<alias>.ts` per bound workflo
67
73
  `src/agents/<alias>.md` per bound agent, and the `.lotics/` type companions — so an existing app
68
74
  arrives fully editable rather than as an archive you have to reconstruct.
69
75
 
70
- **The plan changes after the app is built, and `lotics app regenerate` is how it lands.** Run it
71
- inside the app; the plan is the one `package.json#lotics.plan` remembers, unless `--from` names
72
- another. The generator owns what it emits: `app.json` is derived from the plan and rewritten
73
- whole, and a workflow body is rewritten too, with the text it replaced parked in
74
- `.lotics/regenerate-dropped.patch`. `src/components/` is the one exception — seeded where it is
75
- absent, never rewritten — because it is the hatch: `lotics app eject <screen|section|act>` hands
76
- ONE part of the spec to a component there, and a regeneration carries that hand-over onto the new
77
- spec. The manifest is reconciled by ownership — the generator's aliases replaced, the ones it
78
- retired removed, the ones you added kept — then codegen and `app check` run.
76
+ **The model changes after the app is built, and `lotics app regenerate` is how it lands.** Run it
77
+ inside the app; the model is the one `package.json#lotics.plan` remembers, unless `--from` names
78
+ another. The generator owns what it emits: `app.json` is compiled from the model and rewritten
79
+ whole, and a generated workflow body is rewritten too, with the text it replaced parked in
80
+ `.lotics/regenerate-dropped.patch`. Two things are yours and survive every regeneration:
81
+ `src/components/` (seeded where it is absent, never rewritten), and the body of an act's own
82
+ `workflow` — only the guard region at its top, between the `<lotics:guards>` markers, is the
83
+ generator's. The manifest is reconciled by ownership — the generator's aliases replaced, the ones
84
+ it retired removed, the ones you added kept — then codegen and `app check` run.
79
85
 
80
86
  **Nothing is pushed.** What the live app RUNS changes at `lotics app deploy` and nowhere else: the
81
87
  bundle production serves was built against the bindings it has, so a regeneration that replaced a
@@ -275,29 +281,21 @@ Authoring rules for the body itself: `lotics docs workflows`.
275
281
 
276
282
  ## 7 — Screens
277
283
 
278
- **A plan-built app's screens are its `app.json`, and `@lotics/app-runtime` draws them.** The
279
- register, the record each row opens, its sections, the creates and the acts are all stated by the
280
- plan, so a correction is a change to the PLAN followed by `lotics app regenerate` — or, where the
281
- ruling is about how every app draws, a change to the runtime that reaches every app on its next
282
- install. Every row the runtime draws — a register's subject, a list item, a card, a record's stop, a
283
- log entry — is one anatomy read off the plan's roles (`node_modules/@lotics/app-runtime/AGENTS.md`),
284
- and how a register is laid out is the screen's `presentation` clause. `lotics scaffold check`
285
- prints what each screen will draw, with every value the plan left to the runtime marked
286
- `(default)`, so the printout is where a screen is reviewed before one exists; `lotics app preview`
287
- is where it is looked at.
288
-
289
- **Where the plan has no word for a screen, a section or an act,** the spec names one of the app's
290
- own components — `lotics app eject` writes it from exactly what the runtime was drawing, one part at
291
- a time, and `lotics docs components` says what it is handed. A screen no shape fits is declared in
292
- the plan as `"shape": "custom"` with its slots as roles (`lotics docs model/apps-and-screens`)
293
- — never a shape bent to fit — and then `lotics report` with `wanted` opening `shape <name>`: a custom
294
- slot set that recurs becomes a shape, and the report is how the next build gets it. A component
295
- the kit lacks is built into the kit, never hand-rolled in one app.
284
+ **A model-built app's screens are its `app.json`, and `@lotics/app-runtime` draws them.** The
285
+ register, the record each row opens, its facts and blocks, the acts and the checks are all stated by
286
+ the model, so a correction is a change to the MODEL followed by `lotics app regenerate` — or, where
287
+ the ruling is about how every app draws, a change to the runtime or the kit that reaches every app
288
+ on its next install.
289
+
290
+ **Where the vocabulary has no word for what a record needs,** a `component` block names one of the
291
+ app's own components (`lotics docs components`), and an act whose write `set` cannot say names its
292
+ own `workflow`, which runs after the generated guards. A component the kit lacks is built into the
293
+ kit, never hand-rolled in one app.
296
294
 
297
295
  **A hand-written app composes the kit itself.** Before any JSX, read `lotics docs ui` — the catalog
298
- and the composition grammar; the shape components (`@lotics/ui/lifecycle_desk`,
299
- `@lotics/ui/party_register`, …) and `@lotics/ui/record_page` own the strip, the columns, the fit at
300
- a phone's width and the record's door, so give them rows and slots rather than rebuilding a frame.
296
+ and the composition grammar; the pieces the runtime draws a record with (`RecordFrame`,
297
+ `RecordHeader`, `ChecksCallout`, `FactGroups`, `ActPanel`) and the register's (`Table` with a `Row`
298
+ subject, `FilterBand` with `StatusChips` and `GroupByMenu`) are there to compose rather than rebuild.
301
299
 
302
300
  Two rules for any code the app writes itself — a component or a hand-written screen:
303
301
 
@@ -424,7 +422,7 @@ Once scaffolded, everything below happens locally:
424
422
 
425
423
  ```
426
424
  edit src/workflows/<alias>.ts # or a component, or a query
427
- lotics app preview model.json#<app> --shots shots/ # when the PLAN moved: see it before a table does
425
+ lotics app preview model.json#<app> --shots shots/ # when the MODEL moved: see it before a table does
428
426
  lotics app regenerate --dry-run # then: what the new generation would do to this app
429
427
  lotics app codegen # after any schema change
430
428
  npm run typecheck # honest, because codegen is current