@lotics/ui 27.17.0 → 28.0.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/AGENTS.md +13 -10
- package/MIGRATION.md +64 -0
- package/docs/catalog.md +97 -140
- package/docs/data_entry.md +57 -87
- package/docs/templates.md +95 -57
- package/examples/tpl_item_list.tsx +77 -36
- package/examples/tpl_record.tsx +372 -460
- package/package.json +3 -3
- package/src/checklist.tsx +339 -0
- package/src/comments_button.tsx +108 -0
- package/src/control_surface.ts +13 -0
- package/src/detail_row.tsx +2 -2
- package/src/field_annotations.tsx +1 -1
- package/src/icon.tsx +10 -0
- package/src/icon_button.tsx +17 -1
- package/src/locale.tsx +12 -1
- package/src/stepper.tsx +107 -18
- package/src/stepper_layout.ts +21 -1
- package/src/table.tsx +101 -11
- package/src/pipeline.tsx +0 -231
- package/src/task.tsx +0 -518
- package/src/task_metrics.ts +0 -55
package/docs/data_entry.md
CHANGED
|
@@ -381,88 +381,59 @@ When work crosses departments (sales → operations → accounting), the HANDOFF
|
|
|
381
381
|
changing desks — never an inbox, a notification, or a copied task. Two types:
|
|
382
382
|
|
|
383
383
|
**(A) Same entity** → a STAGE field on the shared record: sections carry OWNER dot tags, and the
|
|
384
|
-
handoff is MANAGED AS
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
**A
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
is a
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
**
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
`
|
|
437
|
-
one lands a label and a control at two more x positions matching nothing above them — a form
|
|
438
|
-
pasted into a list, and with sub-row annotations there is nothing left for it to say.
|
|
439
|
-
`TaskDetail` keeps the FREE-FORM block (a chart, a table, a form with its own submit) — it has no
|
|
440
|
-
name to hang on the indent, so a rule down its left edge is what ties it to the row.
|
|
441
|
-
|
|
442
|
-
**The ⋯ is a COLUMN — the mirror of the control gutter.** Both pinned controls sit outside the
|
|
443
|
-
row's flow and the ROW reserves each edge (`controlWidth` on the left, `actionWidth` on the
|
|
444
|
-
right, each plus the row gap), leaving ONE content box between them that every line spans exactly:
|
|
445
|
-
the title, a caption, a sub-row, a detail block, a nested list. That is what keeps a list where
|
|
446
|
-
only SOME rows carry a menu straight — a menu-less row's content ends on the same x as a
|
|
447
|
-
menu-carrying one's, and so does everything hanging beneath either. A nested list hands the
|
|
448
|
-
parent's reservation back before its own rows re-take it, so the column does not step inward per
|
|
449
|
-
level. Declare `actionWidth={0}` when NO row in the list carries actions, so it pays nothing for a
|
|
450
|
-
column it never uses.
|
|
451
|
-
|
|
452
|
-
**Subtasks are TASKS** — nest a `TaskList` inside the `TaskItem`, and the child carries fields,
|
|
453
|
-
a menu and children of its own. There is no separate subtask shape to outgrow, and no built-in
|
|
454
|
-
chevron: collapsing is the app's call (hold a boolean, render the nested list or don't).
|
|
455
|
-
|
|
456
|
-
**State flows from the LEAVES.** Where a row's position is computable from what it owns, DERIVE
|
|
457
|
-
it — a stored status is a second copy of what the fields already say, and two copies drift. Give
|
|
458
|
-
the parent's ring the bulk gesture where its children ARE the fact (tick it, every child ticks);
|
|
459
|
-
where the ring answers a DIFFERENT field the children do not determine — a date, say — coupling
|
|
460
|
-
them asserts something the operator never said.
|
|
461
|
-
|
|
462
|
-
**A sub-row holds a CONTROL.** Where exactly one field determines a row's status, the sub-row IS
|
|
463
|
-
that field's editor: a read-only copy of it elsewhere on the row puts one value on screen twice,
|
|
464
|
-
under two names. Everything else derived is prose in `TaskCaption` — a read-only pill given a
|
|
465
|
-
field's label reads as pressable when it is not. A `Badge` INSIDE the editor (an
|
|
384
|
+
handoff is MANAGED AS A CHECKLIST — each desk's work on the record. **`checklist`** owns the row
|
|
385
|
+
geometry (the ring column, the connecting line, the rhythm) — compose it, never hand-roll the row.
|
|
386
|
+
A **`ChecklistItem`** carries its title, its own stamp (`trailing`), a short `meta` and an
|
|
387
|
+
optional ⋯ (`menu`); **`ChecklistGroup`** names a phase above the rows it heads. `CaptureRow`
|
|
388
|
+
closes the list as its add-affordance.
|
|
389
|
+
|
|
390
|
+
**`connected` decides which shape you are building.** ON for one record walking positions — the
|
|
391
|
+
line asserts sequence, which is what a desk handoff IS. OFF for N items ticked in any order, where
|
|
392
|
+
a line claims an order the work does not have. It is the same compound either way; they were two
|
|
393
|
+
families until the anatomy turned out to be identical.
|
|
394
|
+
|
|
395
|
+
**A GROUP is a name and nothing else.** No control: a phase owns no completion of its own, so a
|
|
396
|
+
ring there reports without responding — and one identical to the pressable rings beneath it
|
|
397
|
+
invites the press it refuses, which is worse than showing nothing. No BODY: a condition or an act
|
|
398
|
+
hung off a heading leaves the reader working out which of the rows below it they were about. The
|
|
399
|
+
row that OWES the work carries all of it — its wait (`meta`), its condition (`ChecklistNote`) and
|
|
400
|
+
the act that leaves it (`ChecklistActions`).
|
|
401
|
+
|
|
402
|
+
**ROWS TICK ONE AT A TIME.** A parent that ticked its children wrote what the reader never said:
|
|
403
|
+
on a dated ladder, today's date onto milestones that happened on days nobody recorded. Where the
|
|
404
|
+
run is a SEQUENCE, derive position from the highest stamp instead — one click then marks
|
|
405
|
+
everything below it, and a rung the record passed without a recorded day shows a BLANK date, which
|
|
406
|
+
is the honest answer to "when" and stays one click from being filled. That is the whole of the
|
|
407
|
+
bulk gesture, and it invents nothing.
|
|
408
|
+
|
|
409
|
+
**A CHECKLIST REPORTS; IT DOES NOT COLLECT, AND IT DOES NOT PREVIEW.** A field some section owns —
|
|
410
|
+
an assignee, an address, a tax ID — is NAMED as a gap by a **`ChecklistNote`** whose `action` jumps
|
|
411
|
+
to that section. Never an editor in the row: two edit surfaces for one fact, and every row becomes
|
|
412
|
+
two things at once (a status and a form) with nothing to say which one the reader is in. Never a
|
|
413
|
+
rendered value either — an avatar or a badge is a COPY that has to be kept in step with the
|
|
414
|
+
field's real home, and readers start treating the list as the place the value lives. Two
|
|
415
|
+
exceptions, both narrow: **`ChecklistItem.trailing`** for the row's OWN stamp (a tick writes today,
|
|
416
|
+
so a run filled in after the fact needs the correction in reach), and **`ChecklistField`** for a
|
|
417
|
+
value with NO other home — a portal login created at that step, unreachable otherwise.
|
|
418
|
+
|
|
419
|
+
**Prose that grows owns its own line.** `ChecklistItem.meta` is two words riding the title (who it
|
|
420
|
+
waits on, how long); anything that lengthens with the data — "Needs: Tax ID, Delivery address" —
|
|
421
|
+
is a `ChecklistNote`, because a sentence on the title's line wraps into the title and the row stops
|
|
422
|
+
scanning. Neither takes a LABEL: a field is a named value the reader sets, and labelling prose
|
|
423
|
+
makes it read as a field nobody can edit.
|
|
424
|
+
|
|
425
|
+
**Anything that needs SCANNABLE COLUMNS is a `Table`.** The compound once carried a declared VALUE
|
|
426
|
+
column so cells lined up down the list; it aligned, and it read worse — each name ended a quarter
|
|
427
|
+
of the surface from its control, sized for the widest value on the page. Comparing the same four
|
|
428
|
+
values across twenty rows is a table's job.
|
|
429
|
+
|
|
430
|
+
**State flows from the LEAVES.** Where a row's position is computable from what it owns, DERIVE it
|
|
431
|
+
— a stored status is a second copy of what the fields already say, and two copies drift.
|
|
432
|
+
|
|
433
|
+
**One value, one place on the row.** Where a row carries a field at all (`ChecklistField`, the
|
|
434
|
+
no-other-home case), that editor IS the value: a read-only copy of it elsewhere on the row puts
|
|
435
|
+
one value on screen twice, under two names. Everything else derived is prose in a
|
|
436
|
+
`ChecklistNote` — a read-only pill given a field's label reads as pressable when it is not. A `Badge` INSIDE the editor (an
|
|
466
437
|
`InlineSelect renderSelected`) is fine: it inherits the control's own inset.
|
|
467
438
|
|
|
468
439
|
**A status reports what is OUTSTANDING, never what is settled.** A finished row already says so
|
|
@@ -485,7 +456,7 @@ record type) — no human types them; common-but-OPTIONAL tasks appear as `Sugge
|
|
|
485
456
|
the list — a PILL, never a row, so a suggestion can't be mistaken for a task (tap = materialize,
|
|
486
457
|
✕ = dismiss for this record; already-present labels filter out; suggestions never count in
|
|
487
458
|
done/total and pause while a filter narrows the view) — the same suggestion grammar as the billing
|
|
488
|
-
Standard pill. `
|
|
459
|
+
Standard pill. `ChecklistItem.menu` holds an `ActionMenu` (Delete lives BEHIND it, danger-styled and last
|
|
489
460
|
— never a bare ✕ a stray tap can hit), which completes the list's CRUD.
|
|
490
461
|
|
|
491
462
|
Open tasks INFORM the handoff, they NEVER block it: the CTA stays enabled, the count warns, and
|
|
@@ -494,10 +465,9 @@ open tasks carry over. The handoff CTA opens a DIALOG for the receiving desk (as
|
|
|
494
465
|
the record left this register.
|
|
495
466
|
|
|
496
467
|
Tasks PEEK from the register: the done/total column is a pressable compact-`ProgressBar` trigger
|
|
497
|
-
whose popover holds the same `
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
the Task list template's lesson, not the peek's); the popover body is `PopoverContent`'s own
|
|
468
|
+
whose popover holds the same `Checklist` — every `ChecklistItem`'s ring and title, and nothing a
|
|
469
|
+
section already owns (a reassign belongs to the record, not to a peek over fifty rows) — no
|
|
470
|
+
expandable rows (tags/files depth is the task board's lesson, not the peek's); the popover body is `PopoverContent`'s own
|
|
501
471
|
ScrollView (`disableBodyScroll` is ONLY for children that manage their own scroll, like
|
|
502
472
|
`OptionList`). The DRAWER carries a real Tasks SECTION in the Record template's shape (heading +
|
|
503
473
|
the compact meter + the same checklist) — one shared task state per record feeds the column, the
|
package/docs/templates.md
CHANGED
|
@@ -198,8 +198,15 @@ screens — register, per-row action, gated selection, and act-on-many in one. T
|
|
|
198
198
|
|
|
199
199
|
- **One toolbar row** — search + a status `Select` + facet `FilterChip`s LEFT, the New CTA
|
|
200
200
|
RIGHT; a light `SummaryLine` of the filtered view below.
|
|
201
|
-
- **A sortable, `Divider`-separated `Table`** where the record's
|
|
202
|
-
|
|
201
|
+
- **A sortable, `Divider`-separated `Table`** where the record's SUBJECT is the first column —
|
|
202
|
+
who or what the record is FOR, because that is what a reader scans for and what they say out
|
|
203
|
+
loud ("the Harbor Freight one", never "RC-2026-0041"). The KEY is how the SYSTEM refers to the
|
|
204
|
+
record, so it rides the identity cell's supporting line, muted, where the other supporting
|
|
205
|
+
values live; it does NOT take a column of its own, and it is never the row's name. It also does
|
|
206
|
+
not SHRINK there — a key is quoted whole or it is not a key, so a long phone number gives way
|
|
207
|
+
first (it has a `CopyButton` handing over the full value regardless). The record surface names
|
|
208
|
+
itself the same way, or the two disagree about what a record is called. Every row carries a
|
|
209
|
+
leading checkbox; a row that can't take the bulk action gets a
|
|
203
210
|
**disabled** checkbox (the same gating as any blocked line). A row's status is
|
|
204
211
|
`Badge variant="dot"` — never the tonal `Badge` (that's the drawer/header twin; → [composition
|
|
205
212
|
grammar §"Status indicators have a WEIGHT"](./composition.md)) — and the SAME dot vocabulary
|
|
@@ -219,15 +226,18 @@ screens — register, per-row action, gated selection, and act-on-many in one. T
|
|
|
219
226
|
[`tpl_record`](../examples/tpl_record.tsx) in `chrome="drawer"`, not a copy of it. Same
|
|
220
227
|
sections, same order, same rules, because it is the same component: `<TplRecord
|
|
221
228
|
chrome="drawer" code={row.id} />`. A hand-built second record surface is exactly how the two
|
|
222
|
-
drifted apart —
|
|
229
|
+
drifted apart — the progress ladder grouped one way here and flat there, a reference as a
|
|
223
230
|
section here and a field there — so the register owns the REGISTER and nothing else.
|
|
224
|
-
- What the chrome flag changes is only what a nested surface must not do:
|
|
225
|
-
|
|
226
|
-
into), and the back button is left to the Drawer's header. Everything else follows from
|
|
231
|
+
- What the chrome flag changes is only what a nested surface must not do: the back button is
|
|
232
|
+
left to the Drawer's header, since two exits is two exits. Everything else follows from
|
|
227
233
|
width — the layout measures its CONTAINER via `onLayout`, never the window, so the outline
|
|
228
|
-
rail
|
|
229
|
-
|
|
230
|
-
|
|
234
|
+
rail and the mirrored gutters collapse on their own, and the section picker is a popover
|
|
235
|
+
ANCHORED to its bar button rather than a full-page `Modal` that would escape the drawer.
|
|
236
|
+
**The discussion follows the same measure**: docked beside the record where there is room for
|
|
237
|
+
its column, and a SECTION under General where there is not — which a drawer always is. It is
|
|
238
|
+
never behind a control. It was briefly a toggle in the top-right of a narrow page and a
|
|
239
|
+
body-SWAP inside a drawer; both made the reader know a control existed, press it, and give up
|
|
240
|
+
the record to read a note, which is where an unread note goes to be missed.
|
|
231
241
|
- **Linked records** still push an editable screen for that record inside the drawer via the
|
|
232
242
|
hosted `ScreenRouter` — while `canGoBack` the drawer header swaps to a back button + the
|
|
233
243
|
pushed id and the ◀ ▶ sequencer hides; back pops with scroll preserved.
|
|
@@ -314,12 +324,19 @@ billing, and quick-capture templates. Top → bottom:
|
|
|
314
324
|
(author-only edit/delete; attachments as a pressable `FileGrid` → gallery preview; a
|
|
315
325
|
file-capable edit form injected via `renderEditForm`) + THE kit `Composer` (attach via
|
|
316
326
|
`actionsButton`, staged files in its `files` slot) — never a bespoke comment box.
|
|
317
|
-
- **Progress** —
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
327
|
+
- **Progress** — directly UNDER General, because where the record SITS is the second question a
|
|
328
|
+
reader has after what it IS, ahead of its files, its route and its money, which are all
|
|
329
|
+
reference. The desk handoff as the ordered positions it is: a `Checklist connected` where the
|
|
330
|
+
desks are `ChecklistGroup`s (Sales → Operations → Accounting) and the milestones they produce
|
|
331
|
+
are the rows. A GROUP carries no COMPLETION control (a phase owns none, and
|
|
332
|
+
a ring that never responds invites the press it refuses) and no body (a condition hung off a
|
|
333
|
+
heading leaves the reader working out which row it was about). The row that OWES the work
|
|
334
|
+
carries all three: how long it has been owed (`meta`), its condition (`ChecklistNote`, e.g.
|
|
335
|
+
past the due date — with an `action` jumping to the section that owns a missing field rather
|
|
336
|
+
than editing it here) and the single act that leaves the desk (`ChecklistActions` → the handoff
|
|
337
|
+
dialog). Rows tick ONE at a time and position is the HIGHEST stamp, so one click marks
|
|
338
|
+
everything below it without writing dates nobody recorded; a rung it passes shows a BLANK date,
|
|
339
|
+
which is the honest answer to "when" and stays one click from being filled. `meta` is prose
|
|
323
340
|
the reader can't set, never the date that the stage's own field already shows. See § The
|
|
324
341
|
handoff shape for the rules; for a record that genuinely needs a CHECKLIST, the task-list
|
|
325
342
|
grammar is worked in `tpl_item_list`.
|
|
@@ -405,22 +422,21 @@ billing, and quick-capture templates. Top → bottom:
|
|
|
405
422
|
REPLACES the set — derived paperwork, never intake; the desk register above keeps what
|
|
406
423
|
arrived. **READINESS**: a form declares the RECORD FIELDS it reads (`needs`); the fields'
|
|
407
424
|
HOME stays their DATA section (the colocation law) with descriptions naming their
|
|
408
|
-
consumers ("printed on the delivery note"). The picker rows ride
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
DEGRADED-BUT-VALID case (they print blank, filled by hand later): Create stays ENABLED, a
|
|
425
|
+
consumers ("printed on the delivery note"). The picker rows ride a
|
|
426
|
+
`Checklist connected={false} mark="select"` — the SAME compound as Progress, TWO axes apart.
|
|
427
|
+
Unconnected because these forms are a SET the user picks from rather than a sequence, and a
|
|
428
|
+
line between them would claim an order the work does not have; `select` because picking a form
|
|
429
|
+
is a CHOICE, and a filled ring there would read as work already carried out to a reader who
|
|
430
|
+
learned what a ring means one section above. The row NAMES what is missing in a `ChecklistNote` — "Needs: …"
|
|
431
|
+
the actual field labels, muted at rest so gaps show WITHOUT checking, warning once checked
|
|
432
|
+
because it now blocks — and points at the section that owns those fields.
|
|
433
|
+
|
|
434
|
+
**It does not collect them.** The row briefly opened a form of its own here, which made every
|
|
435
|
+
row two things at once (a status and a form) with nothing to say which one the reader was in,
|
|
436
|
+
and put an editor beside a field's real home where its description and its neighbours live. A
|
|
437
|
+
checklist REPORTS; the section that owns a value COLLECTS it. A ready form stays SILENT.
|
|
438
|
+
|
|
439
|
+
Missing fields are the gating law's DEGRADED-BUT-VALID case (they print blank, filled by hand later): Create stays ENABLED, a
|
|
424
440
|
co-located warning `Callout` states the consequence, and the press CONFIRMS via `Alert`.
|
|
425
441
|
Only an empty pick disables, and silently. A form can also be DATA-conditional (the hazardous-goods note renders only
|
|
426
442
|
while the record's Hazardous goods boolean is set — record data may reshape the list,
|
|
@@ -471,40 +487,62 @@ billing, and quick-capture templates. Top → bottom:
|
|
|
471
487
|
below that it collapses to the pinned bar. Sizing the column is the SHELL's job — inner
|
|
472
488
|
blocks are plain `width: "100%"`, never a second `maxWidth` + `alignSelf: "center"`.
|
|
473
489
|
|
|
474
|
-
### The handoff shape —
|
|
490
|
+
### The handoff shape — a CONNECTED checklist, and where it lives
|
|
475
491
|
|
|
476
|
-
One record walking N ordered
|
|
477
|
-
passed between desks — an application through intake → review → production → filing, a claim
|
|
492
|
+
One record walking N ordered positions, where **the position decides what you can see and do**:
|
|
493
|
+
work passed between desks — an application through intake → review → production → filing, a claim
|
|
478
494
|
through submitted → assessed → paid.
|
|
479
495
|
|
|
480
|
-
It is NOT its own template. A handoff is something a RECORD does, so it renders as one section
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
496
|
+
It is NOT its own template. A handoff is something a RECORD does, so it renders as one section OF
|
|
497
|
+
that record — see `tpl_record` § Progress, where the desks (Sales → Operations → Accounting) are
|
|
498
|
+
`ChecklistGroup`s and the milestones they produce are the rows.
|
|
499
|
+
|
|
500
|
+
**Two levels, because a desk and a milestone are not peers.** The ladder was briefly one flat run
|
|
501
|
+
of eight — "Operations" beside "Collected" — which asked the reader to know which name was a PLACE
|
|
502
|
+
and which was an EVENT. Grouping the milestones under the desk that produces them says it in the
|
|
503
|
+
shape instead.
|
|
504
|
+
|
|
505
|
+
**A group is a name and nothing else.** No control: a phase owns no completion of its own, so any
|
|
506
|
+
ring it wore would report without responding — and one identical to the pressable rings beneath it
|
|
507
|
+
invites the press it refuses, which is worse than showing nothing, because the reader clicks and
|
|
508
|
+
the product says nothing back. No BODY either: a condition or an act hung off a heading leaves the
|
|
509
|
+
reader working out which of the rows below it they were about.
|
|
510
|
+
|
|
511
|
+
**Rows tick ONE at a time, and position is the highest stamp.** That is what lets one click say
|
|
512
|
+
"everything up to here": ticking `Delivered` puts the record there and the rungs below it read
|
|
513
|
+
done immediately. The alternative — a parent tick that fills its children — marks the same rows
|
|
514
|
+
but INVENTS their dates, writing today onto milestones that happened on days nobody recorded. A
|
|
515
|
+
rung the record passed without a recorded day shows a BLANK date, which is the honest answer to
|
|
516
|
+
"when" and stays one click from being filled.
|
|
517
|
+
|
|
518
|
+
**A checklist REPORTS.** It does not collect and it does not preview: a field some section owns is
|
|
519
|
+
NAMED as a gap by a `ChecklistNote` whose `action` jumps there, never edited in the row and never
|
|
520
|
+
rendered as a value. The row's OWN stamp (`trailing`) and a value with no other home
|
|
521
|
+
(`ChecklistField`) are the two exceptions.
|
|
522
|
+
|
|
523
|
+
**It replaced a per-desk CHECKLIST without the line, and that swap is the lesson.** Ticking boxes
|
|
524
|
+
described the WORK; it never said where the record SAT, so the reader inferred position from which
|
|
525
|
+
group still had unticked rows — and the handoff CTA sat in a different section entirely, the act
|
|
526
|
+
divorced from the state that gated it. A connected run says the position outright, and the act
|
|
527
|
+
rides the row that owes it. `connected={false}` is for rows that are genuinely N independent
|
|
528
|
+
items; a record's own progress asserts sequence.
|
|
529
|
+
|
|
530
|
+
Built on `checklist` (`Checklist`, `ChecklistGroup`, `ChecklistItem`, `ChecklistNote`,
|
|
531
|
+
`ChecklistActions`, `ChecklistField`), which composes over `Stepper` — `Stepper` renders POSITION,
|
|
494
532
|
this adds the anatomy that turns a position into a workspace.
|
|
495
533
|
|
|
496
534
|
Three rules the anatomy holds, each learned from getting it wrong:
|
|
497
535
|
|
|
498
|
-
- **Only the
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
- **A condition belongs to its
|
|
502
|
-
facts about ONE
|
|
536
|
+
- **Only the OWED row carries an act.** A control on an unreached row invites acting out of
|
|
537
|
+
order — and where position is derived from the highest stamp, one stray press jumps the record
|
|
538
|
+
past the desk that owns it. On a passed row it re-offers something already done.
|
|
539
|
+
- **A condition belongs to its row.** "Sent back", "rejected", "waiting on the customer" are
|
|
540
|
+
facts about ONE position, so they render inside it (`ChecklistNote`) rather than as a `Callout`
|
|
503
541
|
above the run, which reads as "something is wrong with this record" and sits nowhere near the
|
|
504
|
-
control that answers it.
|
|
505
|
-
- **A passed
|
|
506
|
-
|
|
507
|
-
record moves on, and the only fix is direct table access.
|
|
542
|
+
control that answers it — and nowhere near enough to say WHICH row it is about.
|
|
543
|
+
- **A passed row stays correctable** — its stamp is editable, which is how a date recorded wrong
|
|
544
|
+
gets fixed and how a record walks BACK. Otherwise a value typed wrong becomes unreachable the
|
|
545
|
+
moment the record moves on, and the only fix is direct table access.
|
|
508
546
|
- **The stage that OPENS gets stamped by the act that opened it**, and the TERMINAL act is not
|
|
509
547
|
a handoff. Handing off asks whom (a dialog) and writes that answer into the receiving stage's
|
|
510
548
|
own fields — coming up blank re-asks a question just answered. Finishing has no receiving
|