@ai-matrx/records-ui 0.10.3 → 0.16.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,198 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.16.1
4
+
5
+ **`FormSubmitOutcome` is exported.** It is the return type of
6
+ `FormRunnerProps.onSubmit`, so a host supplying its own submit could not type
7
+ its own handler: matrx-frontend's `PublicFormRunner` failed `tsc` on 0.16.0 with
8
+ TS2459, "declares it locally, but it is not exported". A public surface is every
9
+ type a consumer has to name.
10
+
11
+ ### Consumer action
12
+
13
+ None beyond importing it if you implement `onSubmit`.
14
+
15
+ ## 0.16.0
16
+
17
+ **The grid never draws a row before it knows what the person may do.** My own
18
+ regression from 0.12.0's refresh fix, bisected rather than guessed: treating the
19
+ whole first load as "the rows have arrived" let the grid render while the
20
+ table's own read was still in flight, and `useTableRights` answers `NO_RIGHTS`
21
+ (`known: false`) then — so every cell came out as plain text with no editor and
22
+ a click on it did nothing and said nothing. The declaration, the columns and the
23
+ person's own level are answered ONCE per table, so they are a FIRST LOAD; only
24
+ the page re-read is a refresh, and it still never blanks rows somebody is
25
+ already typing in.
26
+
27
+ **A publish gate that can be believed.** This package had not published since
28
+ 0.10.3 while five versions piled up, because `pnpm test` ran the whole live
29
+ component suite against a database several lanes' harnesses share behind one
30
+ advisory lock — each run failed a different handful of timing clauses with no
31
+ overlap. `pnpm test` is now a DETERMINISTIC component suite (the real components
32
+ against recorded real-door answers at this package's own `RecordsDataSource`
33
+ seam, no network, and a call the fixture never saw throws by name) plus a SHORT
34
+ live smoke (declare a table, take a column, write, read back, page, delete).
35
+ Typecheck and both suites run in 27 seconds. The full live suite is
36
+ `pnpm test:live` and runs in its own workflow on the tag and on a schedule,
37
+ reporting red loudly and blocking nothing. Nothing was deleted or loosened.
38
+
39
+ **And the two suites that could not run on a lane's own port** —
40
+ `system-table-shape` and `walk` pinned 127.0.0.1:3005 — now read
41
+ `RECORDS_UI_DEMO_PORT`.
42
+
43
+ ### Consumer action
44
+
45
+ None. No API changed. `pnpm test:record` re-records the door fixture.
46
+
47
+ ## 0.15.0
48
+
49
+ **`ActionInbox` reads the STORE's queue, and the package-owned one is gone.**
50
+ SCR-11 shipped with its own `records_ui_action` Table: agents wrote rows into it
51
+ and it rendered them beautifully. It was a second source of truth. An approval a
52
+ person filed through the store, and the wait the server's own `field_propose`
53
+ produces under `ask`, appeared nowhere in it — which is exactly the thing
54
+ PRODUCTS.md row 6 says the product must not be ("One inbox holds human approvals
55
+ AND the agent's field and table proposals — the same queue, the same right to
56
+ approve").
57
+
58
+ It now reads `custom.work_inbox` and decides through `custom.work_approval_decide`,
59
+ which APPLIES the change in the same transaction as the decision, as the person
60
+ who approved, through the store's own doors.
61
+
62
+ * **Keyboard-first**, because Linear's inbox is the bar: `j`/`k` or the arrows
63
+ move, `a` approves, `d` declines, `o` opens, `r` refreshes. The selected row
64
+ scrolls into view and the header states the keys.
65
+ * **Level-aware, absent rather than dead.** The door answers `actionable` — which
66
+ is `custom.work_approval_may_decide` asked per row — so a row this person may
67
+ not decide renders with NO buttons and says what it is waiting for, instead of
68
+ a greyed-out Approve that fails when clicked. The person who ASKED for a change
69
+ is refused the answer to their own request, by the store, and the row says so.
70
+ * The store's own outcome sentence is shown verbatim ("Rate card is now a column
71
+ on this table"); this component never rewrites it.
72
+
73
+ **Consumer action — BREAKING for anybody who seeded the old queue.**
74
+ `ACTION_TABLE`, `actionDocument`, `QueuedAction`, `QueuedActionSpec` and the
75
+ `seed` prop are removed. Nothing seeds an inbox any more: a person or an agent
76
+ files a real request through `custom.work_approval_request`
77
+ (`client.workApprovalRequest(...)`) and it appears. `ActionInbox` keeps
78
+ `tableId`, `includeSettled`, `onOpenRecord` and `className`. Requires
79
+ `@ai-matrx/records` 0.10.0.
80
+
81
+ ## 0.12.0
82
+
83
+ **Everything 0.11.0 carried — the level-aware screens and the one refusal
84
+ formatter below — is in this release. 0.11.0 was tagged and never published: its
85
+ gate failed, and two of the four failures were real defects rather than stale
86
+ copy. They are what this version fixes.**
87
+
88
+ **A read the person did not ask for no longer takes the cell they are typing in
89
+ off the screen.** `Grid` handed `MatrxDataTable` an `isLoading` that went true on
90
+ every `reload()`, and a cell write reloads the page — while `isLoading` the data
91
+ table renders six SKELETON rows IN PLACE OF the real ones, so committing one cell
92
+ unmounted every cell on the page, the open editor included. In a browser that
93
+ costs the caret and any keystroke inside the window; in the live suite one column
94
+ of a tabbed row saved as nothing, a different one each run, which is what made it
95
+ look like the store rather than the screen. A refresh is now `isFetching` — the
96
+ data table's own refresh line, with the rows kept — and `isLoading` means only
97
+ what it says: there is nothing to show yet.
98
+
99
+ **Enter finishes a long-text cell, and Shift+Enter makes the newline.** It used
100
+ to be the other way round, with Ctrl/Cmd+Enter to save, printed in 10px under the
101
+ box: the key every grid in the world binds did nothing in the columns whose
102
+ values are longest. Airtable is the bar. A json cell takes its newline the same
103
+ way — it was not counted as multi-line at all, so Enter could not save it either.
104
+
105
+ **One version read per record, and a version only ever goes up.** Opening a cell
106
+ asked the store for the record's version, and tabbing on asked again before
107
+ either answer was back; the write between them stored the store's new version,
108
+ and whichever read landed last put the old one back. The next cell of that row
109
+ then carried a version the store had already moved past, and a person tabbing
110
+ through their own row was shown a conflict with themselves.
111
+
112
+ **A borrowed value and a total can be built over a Person or a File again.**
113
+ `ValueOnTheOtherSide` offers the columns the relation's target Table DECLARES —
114
+ and a Person column points at the person kernel, which has 441 records and zero
115
+ Field rows, so it offered nothing and said the table had no columns. The store
116
+ reads the far side as `custom.record_values(…) -> pick`, so a pick names a key
117
+ those records CARRY: when the target declares no Fields, the panel reads one of
118
+ its records through the ordinary read door and offers the keys it has.
119
+
120
+ ### Consumer action
121
+
122
+ None beyond 0.11.0's (below): no API changed. Requires `@ai-matrx/records`
123
+ >= 0.7.0 for `custom.my_levels`.
124
+
125
+ ## 0.11.0 — tagged, never published
126
+
127
+ **The screens never invite an action the ladder will refuse.** The 19–20
128
+ September verdict (sixth pass), measured against Airtable, Notion and Linear:
129
+ "None of these three products lets you start an action it already knows it will
130
+ reject."
131
+
132
+ **Every control follows the level the STORE answers.** The rights question was a
133
+ port with two honest answers and no third — nothing offered, or
134
+ `storeDecidesRights`: yes to everything for every non-kernel table, for
135
+ everybody, and let the door refuse afterwards. That second one is what every
136
+ person in the product met, and it is the defect written down. `useTableRights`
137
+ now asks `custom.my_levels` (a table is a record), `useRecordRights` asks for one
138
+ record, and `useRowRights` asks for a whole page in ONE call — and only when the
139
+ table's own level does not already admit editing, because a record shared at
140
+ Editor inside a table held at Viewer is exactly what the verdict found and the
141
+ grid labelled that row "viewer" while the store let its holder write to it.
142
+
143
+ `src/rights.ts` is the ONE place a level becomes a screen: six capabilities —
144
+ read, comment, write, remove, share, structure — each keyed to the rung the DOOR
145
+ itself asks for. Every `false` carries the sentence that says why, naming the
146
+ level it would take and who can give it: *"You are Viewer here, and it takes
147
+ Editor to change anything here. Ask somebody who is Admin on it to move you up."*
148
+ Absent AND explained. A cell a person may not write is plain text that says so
149
+ on hover; Tab walks PAST a row they may not write rather than opening an editor
150
+ in it; Delete is absent on those rows; Share is absent below Admin; the field
151
+ panel and the table settings say what changing the shape of a table takes.
152
+
153
+ **One formatter, and every refusal goes through it.** `src/plainWords.ts`;
154
+ `Refusal.tsx` renders what it returns and NOTHING else — no `error.message`, no
155
+ `error.hint`, no SQLSTATE where a person reads. Filtering the hint was not
156
+ enough, structurally: the store builds its access refusals with `format()` from
157
+ THE DOOR'S OWN NAME, so the machine identity is inside the sentence written for
158
+ the person ("You do not have access to this record, so `custom.record_update`
159
+ may not write to it"). The formatter drops the machine clause by SHAPE, keeps
160
+ the store's own words where they are a person's words, and ADDS A REMEDY keyed
161
+ off the refusal code — a refusal with nothing to do about it is a dead end with
162
+ an explanation. What it drops is kept for an engineer on the notice and in a
163
+ screen-reader-only line. Nine screens that had captured `error.message` into
164
+ their own state now go through it too.
165
+
166
+ **A borrowed value and a total no longer ask for an internal key.** "The value
167
+ to read" was a free-text box with the placeholder `name`; the relation already
168
+ says which Table it points at, so both now offer that Table's columns BY NAME.
169
+
170
+ **All sixteen column types open their own editor.** Four editor states that drew
171
+ a paragraph rather than a box — a choice list loading, a choice list whose Table
172
+ is empty, a multi-choice row, a worked-out column — carried no cell id, so the
173
+ cell could not identify itself. Verified keyboard-only against the live store:
174
+ sixteen open, nine typed kinds land, the date included.
175
+
176
+ **Internal names, swept, by a guard that reads rendered text node by node.** Gone
177
+ from where a person reads: the derived key under the field panel's name box; the
178
+ machine key as a history label and raw JSON for unknown history values; the
179
+ conflict line's key and two JSON blobs; the record panel's provenance rows as
180
+ JSON; a truncated uuid standing in for an unnamed subject's name; JSON dumps in
181
+ cells and in the grid's conflict line.
182
+
183
+ ### Consumer action
184
+
185
+ **None for the three mount sites in matrx-frontend** — they already pass
186
+ `letTheStoreDecideRights`, which now means what it says: bind no `rights` port
187
+ and the package asks the store. `storeDecidesRights` is DEPRECATED and answers
188
+ "not known": it is synchronous, the honest answer needs a door call, and a port
189
+ bound there would OVERRIDE the store's answer. A host that binds `rights` of its
190
+ own still wins, and `tableRightsAt(level)` is the one way to build that answer
191
+ by hand — it takes the LEVEL, never six booleans, so nobody can hand a screen a
192
+ combination the ladder cannot produce.
193
+
194
+ Requires `@ai-matrx/records` >= 0.7.0 for `custom.my_levels`.
195
+
3
196
  ## 0.10.3
4
197
 
5
198
  Reissues the unpublished 0.10.2 candidate. Its live-store release gate exposed