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