@ai-matrx/records-ui 0.28.0 → 0.38.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,329 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+
4
+
5
+ ## 0.38.0 — 2026-09-20
6
+
7
+ **0.37.0's gate went red on the tarball, not on this package.** The packed
8
+ consumer imports `@ai-matrx/records` from npm, and the published copy did not
9
+ yet carry `openCaptureQueue` — a door the offline capture screens here already
10
+ call. That is the tarball canary doing exactly its job: green in the monorepo,
11
+ absent for an npm consumer. `@ai-matrx/records` 0.26.0 carries it, and this
12
+ release is the first that can resolve against it.
13
+
14
+
15
+ ## 0.37.0 — 2026-09-20
16
+
17
+ **Three unhandled rejections were failing the release gate while every test
18
+ passed.** `demo/fixtures/doors.json` had no recorded answer for
19
+ `custom.enrich_cells`, a door the Enrich screens started calling — and the
20
+ recorded-doors fake deliberately REFUSES to invent one, because an empty answer
21
+ there would be a green test about nothing. That refusal arrived as an unhandled
22
+ rejection, so vitest reported `12 passed / 79 passed` and exited non-zero, and
23
+ 0.36.0 never reached npm. The fixtures are re-recorded against the live store;
24
+ the suite is now 12 files / 79 tests with no errors.
25
+
26
+
27
+ ## 0.37.0 — 2026-09-20
28
+
29
+ **The capture sheet, run on a phone, with or without a signal (PRODUCTS row 15).**
30
+
31
+ * `CaptureRun` — a DECLARED capture sheet, one question at a time, big targets,
32
+ the camera and the microphone through `accept`/`capture` (which is what opens
33
+ them on a phone rather than a file browser), and the durable queue from
34
+ `@ai-matrx/records@0.25.0` underneath. The whole sheet is fetched ONCE by
35
+ `custom.capture_open` and everything after that happens on the phone: a
36
+ validation that needs a round trip does not exist in a basement. It never says
37
+ *saved* when it means *queued* — the thank-you screen says which, the waiting
38
+ count is on screen the whole time, and "last synced" is a real moment read from
39
+ the queue. A member who may only look is told in words on the FIRST screen,
40
+ because `may_capture` comes from the door with a sentence attached.
41
+ * `CaptureSheet` gains `sheetId` and delegates to `CaptureRun` when it is given.
42
+ One component, two modes: with a sheet it runs what somebody DECLARED (the crew
43
+ case — twelve people, one round, the same questions); without one it stays the
44
+ ad-hoc capture that guesses which Field a reading goes in, which is right for a
45
+ one-off and wrong for a crew. No second capture component.
46
+
47
+ ## 0.36.0
48
+
49
+ **"Not between ten at night and seven in the morning" — the two settings the notify
50
+ editor could not offer, and the summary you can ask to see.**
51
+
52
+ `NotifyRuleEditor` could say WHICH view and, out of a two-word vocabulary, roughly
53
+ how often. There was no way to say "Monday at eight" and no way at all to say when
54
+ NOT to be told, so a subscription that texts on a new lead texted at 03:00. It now
55
+ asks for the cadence (from the store's own catalogue — a picker can never offer a
56
+ word the runner does not act on), the channel, the schedule, and both ends of a
57
+ quiet-hours block, and it says plainly that a send inside those hours is HELD until
58
+ they end rather than dropped.
59
+
60
+ It also stops assembling the Rule itself and writes through `subscriptionDeclare`.
61
+ The store is the one place that knows which cadences the runner honours and which
62
+ quiet hours it can parse, and it refuses the rest by name — so the sentence a person
63
+ reads on a refusal is the store's own.
64
+
65
+ `SubscriptionsPanel` gained the two answers anybody looking at a notifications list
66
+ actually wants: **when the next summary is due** (computed by the same store
67
+ functions the runner uses, so the screen and the runner cannot drift — and absent
68
+ rather than wrong on a muted subscription) and when it last told you anything. Plus
69
+ **"Send me a preview now"**, which renders the summary it would send at this moment
70
+ with what arrived, what left and what changed NAMED. It assembles only: nothing is
71
+ delivered, nothing is recorded and the watermark does not move, so pressing it never
72
+ costs somebody a text and never robs the real Monday summary of a week of news.
73
+
74
+ Needs `@ai-matrx/records` ≥ 0.26.0.
75
+
76
+
77
+ ## 0.36.0 — 2026-09-20
78
+
79
+ **0.35.0 never reached npm: the demo harness asked the store for ten thousand
80
+ rows in one page.**
81
+
82
+ `custom.read_records` serves at most a thousand rows in one page and now REFUSES
83
+ a bigger ask outright (`22023`, `PAGE-1`) rather than quietly serving fewer —
84
+ which is the right behaviour and exactly the kind of honesty this package is
85
+ built on. The harness's reset asked for 10,000, so it read nothing, threw, and
86
+ took the whole release gate down with it. It pages now, a thousand at a time,
87
+ until a short page says it is done. The ceiling is the store's to raise
88
+ (`custom/page_size_ceiling`); a caller's job is to page.
89
+
90
+ Proven against the LIVE store with the demo server up: `demo/live-smoke.test.tsx`
91
+ green, 1 passed.
92
+
93
+ Also in this release, from 0.34.0/0.35.0 and this lane:
94
+
95
+ - **"Who changed this?" is drawn for a person who cannot edit the cell.**
96
+ `columnForField` chose between the cell shell and a plain value on whether
97
+ there was an EDIT SESSION, and the grid passes `null` there whenever the table
98
+ is not this person's to write — so on a viewer, a portal and every read-only
99
+ embed the control vanished, while `gridEditing.tsx` promised it in its own
100
+ comments. `GridCell` now takes `editing: GridEditing | null`; a null session
101
+ means no editor, and nothing else.
102
+ - The tarball canary names `HistoryPanel`, `CommentThread` and
103
+ `FieldHistoryPanel`, so a screen that is green in the monorepo and absent from
104
+ an npm consumer's tarball is caught at the gate.
105
+
106
+
107
+ ## 0.35.0
108
+
109
+ **Per-record AI enrichment: the badge on every agent-owned cell, and the panel behind it.**
110
+
111
+ `EnrichBadge` is drawn beside every value a model owns, always — not on hover and
112
+ not behind a toggle, because a value a machine guessed and a value a person typed
113
+ are different facts and the difference has to survive onto the screen. Hovering it
114
+ gives the rest: who, when, what was read, how sure the model was, what it nearly
115
+ said instead, and the one control that matters — taking the cell back. `stale`
116
+ rides beside it as a second WORD rather than a colour, because "this is six weeks
117
+ old" is a sentence and a hue has to be explained every time.
118
+
119
+ `useEnrichCells` fetches a whole page's provenance in ONE `custom.enrich_cells`,
120
+ and `Grid` uses it for the ids it is actually drawing.
121
+
122
+ `EnrichPanel` is rewritten. It was a per-record "refill these fields" button over a
123
+ port nothing bound; it is now the whole of an enrichment in one screen, in the
124
+ order somebody thinks in: what should go in this column · what it may look at ·
125
+ how often to check it again · what it has cost, per run and per row · SEE IT ON
126
+ FIVE ROWS · and only then, turn it on. Setting one up from a column's own menu
127
+ lands here too, so "Let AI fill" is not a button that leads nowhere.
128
+
129
+ `TableSettings` gains that menu entry beside Edit and Remove, and marks a column a
130
+ model already fills in.
131
+
132
+ **BREAKING:** `EnrichPanel` no longer takes `recordId`. It is a panel about a
133
+ COLUMN and its whole Table, which is what "keep this filled in" is about; the old
134
+ signature could only ever refill one record.
135
+
136
+ The package's own gate now resolves `@ai-matrx/records` to the workspace SOURCE
137
+ rather than the last published tarball — before that, a suite could only exercise
138
+ doors already on npm, so every new door arrived untested through the one gate meant
139
+ to catch it.
140
+
141
+ ## 0.34.0
142
+ ## 0.33.0 — 2026-09-20
143
+
144
+ **"Who changed this?" rendered zero controls for the person most likely to ask
145
+ it.**
146
+
147
+ `gridEditing.tsx` says in its own comments that the cell's history control is
148
+ offered at VIEWER too — "a person who may not edit a cell is exactly the person
149
+ who wants to know who did". `columnForField` did not agree: it chose between the
150
+ cell shell and a plain value on whether there was an EDIT SESSION, and the grid
151
+ passes `null` there whenever the table is not this person's to write. So on a
152
+ viewer, a portal and every read-only embed the shell vanished and took the
153
+ control with it. The shell is now drawn whenever there is an editor OR a
154
+ question to ask, and `GridCell` takes `editing: GridEditing | null` — a null
155
+ session means no editor, and nothing else. `src/who-changed.test.tsx` covers
156
+ it; its load-bearing clause fails on the old gate.
157
+
158
+
159
+ ## 0.32.0
160
+
161
+ **The import wizard, rebuilt on the store's own import doors (SCR-10 / SCR-N-7).**
162
+
163
+ `ImportWizard` parsed a file and then called `recordWrite` **once per row**. At
164
+ the five thousand rows a real spreadsheet holds that is five thousand HTTP round
165
+ trips; there was no duplicate check, so importing a corrected file made a second
166
+ copy of everything; nothing recorded that a value came from a file; and a
167
+ refusal carried a row NUMBER into a file the screen no longer had, so "row 3,184
168
+ was refused" was information nobody could act on. `onProposeField` handed a
169
+ header to a callback and the platform forgot about it.
170
+
171
+ The bytes are still parsed here — Postgres cannot unzip a workbook — and
172
+ **everything after the parse is the store's**:
173
+
174
+ - **The plan, before anything is written.** `importPlan` answers what each
175
+ column is, reading the parity map AND this organization, and the screen shows
176
+ the kind, the store's own sentence saying why, and the first values. Every
177
+ column's destination is a dropdown a person can change, and what they change
178
+ is what the store is sent.
179
+ - **A duplicate key, chosen on the screen** ("same row means the same …"), with
180
+ "leave it alone" or "bring it up to date".
181
+ - **Batches of 250** through `importRows`, with a live count, and **one outcome
182
+ per row**: landed, already here, or refused **with the store's own sentence
183
+ and the row from the person's own file** beside it. The outcome table filters
184
+ to refused / already-here and is the click-through SCR-10 asks for.
185
+ - **The same file twice writes nothing.** The file's sha-256 is the run's
186
+ identity; the screen shows when it ran and what landed, and never sends a row.
187
+ Where Web Crypto is genuinely absent the screen SAYS it cannot recognise the
188
+ file again rather than inventing a hash.
189
+ - **Columns this table does not have go to the ONE approvals inbox** by default,
190
+ and the screen says so on the control itself. "Add them straight away" is
191
+ offered only to an admin and is **absent, not disabled,** for anybody else —
192
+ who are not stuck, because the default still files their column.
193
+ - **One word per type, everywhere.** The wizard reads `FIELD_TYPE_CHOICES`, the
194
+ same list the "Add field" panel offers, so a column is "Money" in both places.
195
+
196
+ `ImportWizardProps.onProposeField` is **gone**: the offer is now a real change
197
+ waiting in a real queue, not a callback the host had to invent a home for. Both
198
+ existing call sites (`TablesHome`, `TablePage`) already pass only `tableId` and
199
+ `onDone`.
200
+
201
+ New in the blocking gate: `src/import-wizard.test.tsx`, five clauses against
202
+ door answers recorded from the live store. Its red twin is `demo/import.red.tsx`
203
+ (the screen as it was), which fails on all three arms.
204
+
205
+ Requires `@ai-matrx/records` >= 0.19.0.
206
+ ## 0.31.0
207
+
208
+ **A record's past is a list of VERSIONS, and an agent names the person it acted for.**
209
+
210
+ `HistoryPanel` listed each field's LATEST value with the author of the last
211
+ write beside it. Useful, and not a history: it could not say what a price was
212
+ before, when it changed, what else changed with it, or put any of it back. It
213
+ reads `custom.record_history` now — one line per VERSION, newest first, each
214
+ naming who, when, the operation in the word somebody used, and every field that
215
+ moved with its before and its after, the Rule version that produced a derived
216
+ value and the evidence behind it. Keyboard-first: Up and Down walk the versions,
217
+ Enter opens one, R starts a restore.
218
+
219
+ **The put-it-back action names what will change before it does.** Pressing
220
+ Restore asks the store what the write would do and shows it — *"Price 1,100 →
221
+ 1,200, Notes cleared"* — and only the second press writes. It says out loud that
222
+ the write is a new version and nothing in the chain is erased, which is the
223
+ thing people are afraid of about an undo button.
224
+
225
+ **`FieldHistoryPanel` is new: "who changed this?", asked of a CELL.** Every grid
226
+ cell now carries the question (a quiet control on hover or focus, and `H` from
227
+ the keyboard at viewer too — a person who may not edit a cell is exactly the
228
+ person who wants to know who did), and the answer is that COLUMN's history
229
+ across the whole Table, with "put this one back" on each row.
230
+
231
+ **`CommentThread` stops keeping its own store.** It declared a package-owned
232
+ system Table called `records_ui_comment` into whichever organization opened the
233
+ screen — the same second store lane FORMS removed for forms and lane DASHBOARDS
234
+ removed for dashboards — and here the rung was wrong in BOTH directions: a
235
+ `commenter` could not comment at all, and anybody with write on the package's
236
+ comment Table could comment on a record they may not open. It reads
237
+ `custom.comment_thread` and writes `custom.comment_write` now, with the people
238
+ NAMED, a comment anchored to one Field, resolve and re-open, and an `@` mention
239
+ picked from the organization's own roster. Whether this person may comment is
240
+ the STORE'S answer, in the same call that answers the thread.
241
+
242
+ **Two defects the SCREENSHOTS found**, neither visible to a test, a type or a
243
+ door: a version's timestamp rendered `9/20/2026, 7:12:37 AM` and wrapped onto two
244
+ lines, doubling the height of every row — it is a relative time now (`12 minutes
245
+ ago`, `Tue 14:12`, then a date) with the exact moment on hover, which is what
246
+ Linear and Notion do and is also what fits. And the cell's "who changed this?"
247
+ control was `display:none` until hover, so it was unreachable by the keyboard
248
+ and invisible to anything driving the screen; it is `opacity-0` now and appears
249
+ on hover OR focus.
250
+
251
+ **Consumer action:** `COMMENT_TABLE` is gone — it described a store that no
252
+ longer exists and nothing outside this package used it. `HistoryPanel` gains an
253
+ optional `onAskAboutField`, `CommentThread` an optional `fieldKey`, and `Grid`,
254
+ `ViewSwitcher` and `columnForField` an optional `onAskWhoChanged`; every one is
255
+ additive and absent means the control is not drawn. Needs
256
+ `@ai-matrx/records` >= 0.18.0.
257
+
258
+ ## 0.30.0
259
+
260
+ **The notification editor stops asking for the notifier's own reader.**
261
+
262
+ `NotifyRuleEditor` listed subscriptions with `client.aggSubscriptions()` and read
263
+ its cadences from `client.aggSubscriptionCadences()`. Both are `custom.agg_*` —
264
+ the NOTIFIER's internals — and neither holds a client grant, so a signed-in
265
+ person opened this panel on *"permission denied for function agg_subscriptions"*
266
+ and the cadence picker silently fell back to a word typed into the file. Lane
267
+ TEST-BENCH photographed that refusal on the try-everything page on 2026-09-20.
268
+
269
+ It now asks the person's own doors: `client.subscriptions({ table_id })`, which
270
+ answers what is addressed to them plus — only where they hold admin on this
271
+ Table — anyone's over it, narrowed to Tables they can already open; and
272
+ `client.subscriptionCadences()`, which delegates to the notifier's one array.
273
+
274
+ Two consequences, both visible: the list is now scoped to the Table the editor is
275
+ about (it used to answer the whole organization), and a **muted** subscription
276
+ appears with the sentence *"Switched off — it tells nobody until somebody
277
+ switches it back on."* The notifier's reader excluded muted rows, so switching one
278
+ off used to make it vanish from this list and read as gone rather than as off.
279
+
280
+ **Consumer action:** requires `@ai-matrx/records` 0.17.0 or above.
281
+
282
+ ## 0.29.0
283
+
284
+ **A saved view can now say how the table is SECTIONED, how wide its columns are,
285
+ and how tall its rows are — and the grid windows itself and stops implying it
286
+ counted everything.**
287
+
288
+ The shared data table gained grouping, controlled column widths, virtualization
289
+ and an honest count in design-system 0.23.0. This is the store's half, and it
290
+ rides the document a view already has (`records_ui_view.presentation`): no new
291
+ column, no new table, and an agent sets any of it with the same record write it
292
+ already uses for colors and formats.
293
+
294
+ - **Sections by a Field.** `presentation.grouping` names a Field key, with
295
+ optional per-Field subtotals, a section order and the sections to keep shut.
296
+ Grouped by Status, the table draws one collapsible header per status with a
297
+ count of records on it — and a view that names a Field but no subtotals gets a
298
+ COUNT, never a total nobody asked for.
299
+ - **Remembered column widths.** `presentation.widths` is sparse and keyed by
300
+ Field key, so a view saved before widths existed still reads. Drag a column
301
+ wider and the width is kept.
302
+ - **A row height.** `presentation.rowHeight`, clamped to 24–96px. A view asking
303
+ for anything outside that is dropped rather than obeyed, because a saved look
304
+ must not be able to make a table unreadable.
305
+ - **The grid windows itself.** Virtualization is on for every record table; the
306
+ primitive's own threshold decides when it engages, so short tables are
307
+ untouched and a very long one scrolls.
308
+ - **The count says what it covers.** The search and filters in the grid's
309
+ toolbar run in the browser over the page the read door returned, so the table
310
+ now states that in plain English instead of letting the loaded count pose as
311
+ the whole table — and says the total is unknown when the door cannot give one.
312
+ - **The person's own changes are saved back onto the view.** Picking a column to
313
+ group by, shutting a section and resizing a column travel `Grid`'s new
314
+ `onPresentationChange` into the door every other view change already uses, so
315
+ `TablePage` writes them onto the view record with the version it read. A host
316
+ that binds no door still gets all three for the visit.
317
+
318
+ ### Consumer action
319
+
320
+ - Nothing to change. Every new field is optional and a view that carries none of
321
+ them renders exactly as it did.
322
+ - A host that mounts `Grid` directly and wants these remembered binds
323
+ `onPresentationChange` and saves the look onto its view record;
324
+ `ViewSwitcher` and `TablePage` already do.
325
+ - Needs `@ai-matrx/design-system` 0.23.0 or above.
326
+
3
327
  ## 0.28.0
4
328
 
5
329
  **The publish canary installs every peer this package declares.** It named
@@ -12,6 +336,8 @@ installed in a pass of their own so each brings its own peers (`react-is` is
12
336
  `recharts`'). 0.27.0 was tagged and never published; its tag was left where it
13
337
  is.
14
338
 
339
+ ## 0.27.0
340
+
15
341
  **A first-time admin could not make a table, and the gate could not see it.**
16
342
 
17
343
  Creating one showed a red refusal and made a table anyway; that table opened with
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @ai-matrx/records-ui
2
2
 
3
- THE canonical screens of the AI Matrx record store. Every component here reads
4
- and writes ONLY through [`@ai-matrx/records`](../records/README.md), renders
5
- with `@ai-matrx/design-system`'s own primitives and data table, and carries
3
+ The platform's primary record UI components. Every component here reads and
4
+ writes ONLY through [`@ai-matrx/records`](../records/README.md), renders with
5
+ `@ai-matrx/design-system`'s own primitives and data table, and carries
6
6
  `@ai-matrx/alchemy`'s menu where a person reaches for one.
7
7
 
8
8
  There is no bespoke per-feature record UI in this platform. A feature that needs