@ai-matrx/records-ui 0.30.0 → 0.40.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,318 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.40.0
4
+
5
+ **Pressing "Settings" on a table crashed the whole page, and had since the
6
+ enrichment panel was added.**
7
+
8
+ Lane LAND-SCREENS pressed it in a production build of the app, signed in as the
9
+ admin who owns the table, and the route went to the application's global error
10
+ boundary with React #310 — *"Rendered more hooks than during the previous render."*
11
+ Everything on that panel went with it: rename the table, add a column, take one
12
+ off, reorder, delete the table, and the enrichment panel itself, which lives behind
13
+ a column's own row and was therefore unreachable in the shipped build.
14
+
15
+ **The cause, and why no suite saw it.** `TableSettings` declared `useState` for the
16
+ enrichment panel BELOW three early returns, one of which fires on the FIRST render
17
+ of every real mount: a host that lets the store decide rights binds no `rights`
18
+ port, so `useTableRights(table.data)` answers `NO_RIGHTS` until `useTable` and
19
+ `custom.my_level` come back. Render one ran eight hooks and returned the "you may
20
+ not change the structure" line; render two, a moment later, ran nine. Every test in
21
+ this package binds `rights: () => tableRightsAt("admin")`, which never takes that
22
+ return — so the panel was only ever rendered with nine hooks and the order never
23
+ changed.
24
+
25
+ `src/TableSettings.hooks.test.tsx` mounts the panel with NO rights port, which is
26
+ the seat the product actually uses, and fails with that exact error on the previous
27
+ hook order.
28
+
29
+ Also: `src/form-publish.test.tsx` had two TS7006 implicit-`any` parameters that had
30
+ `tsc --noEmit` red on this package on `main`. Typed; the package typechecks clean.
31
+
32
+ ## 0.39.0
33
+
34
+ **The kanban view becomes the board, with the store judging every drag.**
35
+
36
+ `PipelineBoard` renders THROUGH the view that already exists — `ViewSwitcher`'s
37
+ kanban layout — rather than beside it. It draws the very same rows the view holds;
38
+ what it adds is the store's judgement.
39
+
40
+ * **Drag between columns.** A drop asks `custom.pipeline_transition_refusal` first
41
+ and either writes the move or leaves the card exactly where it was, carrying the
42
+ store's own sentence, verbatim. Nothing is written on a refusal, so there is no
43
+ snap-back to animate — the card never moved.
44
+ * **A stage's demands, prompted on the way in.** When the refusal names the columns
45
+ a stage needs, they appear on the card with a field each and one button, so the
46
+ person finishes the move instead of being sent away from it. Until every demand is
47
+ answered there is no button to press, rather than one that looks live.
48
+ * **Column headings carry the store's numbers** — the count, the chosen measure's
49
+ total, and the column's limit — over every record this person may see, not over
50
+ the page the screen happened to load. Which column is totalled is a fact about the
51
+ VIEW, saved on it like its colours.
52
+ * **A work-in-progress limit shows on the column it belongs to**, and a column over
53
+ its limit says so in a sentence rather than only turning red.
54
+ * **A viewer's card has no drag handle at all.** Absent rather than dead.
55
+
56
+ A table that is not a pipeline draws the plain kanban exactly as before; the
57
+ decision is the Table's own `stage_field`, asked once, never guessed from the shape
58
+ of a Choice column.
59
+
60
+
61
+
62
+
63
+ ## 0.38.0 — 2026-09-20
64
+
65
+ **0.37.0's gate went red on the tarball, not on this package.** The packed
66
+ consumer imports `@ai-matrx/records` from npm, and the published copy did not
67
+ yet carry `openCaptureQueue` — a door the offline capture screens here already
68
+ call. That is the tarball canary doing exactly its job: green in the monorepo,
69
+ absent for an npm consumer. `@ai-matrx/records` 0.26.0 carries it, and this
70
+ release is the first that can resolve against it.
71
+
72
+
73
+ ## 0.37.0 — 2026-09-20
74
+
75
+ **Three unhandled rejections were failing the release gate while every test
76
+ passed.** `demo/fixtures/doors.json` had no recorded answer for
77
+ `custom.enrich_cells`, a door the Enrich screens started calling — and the
78
+ recorded-doors fake deliberately REFUSES to invent one, because an empty answer
79
+ there would be a green test about nothing. That refusal arrived as an unhandled
80
+ rejection, so vitest reported `12 passed / 79 passed` and exited non-zero, and
81
+ 0.36.0 never reached npm. The fixtures are re-recorded against the live store;
82
+ the suite is now 12 files / 79 tests with no errors.
83
+
84
+
85
+ ## 0.37.0 — 2026-09-20
86
+
87
+ **The capture sheet, run on a phone, with or without a signal (PRODUCTS row 15).**
88
+
89
+ * `CaptureRun` — a DECLARED capture sheet, one question at a time, big targets,
90
+ the camera and the microphone through `accept`/`capture` (which is what opens
91
+ them on a phone rather than a file browser), and the durable queue from
92
+ `@ai-matrx/records@0.25.0` underneath. The whole sheet is fetched ONCE by
93
+ `custom.capture_open` and everything after that happens on the phone: a
94
+ validation that needs a round trip does not exist in a basement. It never says
95
+ *saved* when it means *queued* — the thank-you screen says which, the waiting
96
+ count is on screen the whole time, and "last synced" is a real moment read from
97
+ the queue. A member who may only look is told in words on the FIRST screen,
98
+ because `may_capture` comes from the door with a sentence attached.
99
+ * `CaptureSheet` gains `sheetId` and delegates to `CaptureRun` when it is given.
100
+ One component, two modes: with a sheet it runs what somebody DECLARED (the crew
101
+ case — twelve people, one round, the same questions); without one it stays the
102
+ ad-hoc capture that guesses which Field a reading goes in, which is right for a
103
+ one-off and wrong for a crew. No second capture component.
104
+
105
+ ## 0.36.0
106
+
107
+ **"Not between ten at night and seven in the morning" — the two settings the notify
108
+ editor could not offer, and the summary you can ask to see.**
109
+
110
+ `NotifyRuleEditor` could say WHICH view and, out of a two-word vocabulary, roughly
111
+ how often. There was no way to say "Monday at eight" and no way at all to say when
112
+ NOT to be told, so a subscription that texts on a new lead texted at 03:00. It now
113
+ asks for the cadence (from the store's own catalogue — a picker can never offer a
114
+ word the runner does not act on), the channel, the schedule, and both ends of a
115
+ quiet-hours block, and it says plainly that a send inside those hours is HELD until
116
+ they end rather than dropped.
117
+
118
+ It also stops assembling the Rule itself and writes through `subscriptionDeclare`.
119
+ The store is the one place that knows which cadences the runner honours and which
120
+ quiet hours it can parse, and it refuses the rest by name — so the sentence a person
121
+ reads on a refusal is the store's own.
122
+
123
+ `SubscriptionsPanel` gained the two answers anybody looking at a notifications list
124
+ actually wants: **when the next summary is due** (computed by the same store
125
+ functions the runner uses, so the screen and the runner cannot drift — and absent
126
+ rather than wrong on a muted subscription) and when it last told you anything. Plus
127
+ **"Send me a preview now"**, which renders the summary it would send at this moment
128
+ with what arrived, what left and what changed NAMED. It assembles only: nothing is
129
+ delivered, nothing is recorded and the watermark does not move, so pressing it never
130
+ costs somebody a text and never robs the real Monday summary of a week of news.
131
+
132
+ Needs `@ai-matrx/records` ≥ 0.26.0.
133
+
134
+
135
+ ## 0.36.0 — 2026-09-20
136
+
137
+ **0.35.0 never reached npm: the demo harness asked the store for ten thousand
138
+ rows in one page.**
139
+
140
+ `custom.read_records` serves at most a thousand rows in one page and now REFUSES
141
+ a bigger ask outright (`22023`, `PAGE-1`) rather than quietly serving fewer —
142
+ which is the right behaviour and exactly the kind of honesty this package is
143
+ built on. The harness's reset asked for 10,000, so it read nothing, threw, and
144
+ took the whole release gate down with it. It pages now, a thousand at a time,
145
+ until a short page says it is done. The ceiling is the store's to raise
146
+ (`custom/page_size_ceiling`); a caller's job is to page.
147
+
148
+ Proven against the LIVE store with the demo server up: `demo/live-smoke.test.tsx`
149
+ green, 1 passed.
150
+
151
+ Also in this release, from 0.34.0/0.35.0 and this lane:
152
+
153
+ - **"Who changed this?" is drawn for a person who cannot edit the cell.**
154
+ `columnForField` chose between the cell shell and a plain value on whether
155
+ there was an EDIT SESSION, and the grid passes `null` there whenever the table
156
+ is not this person's to write — so on a viewer, a portal and every read-only
157
+ embed the control vanished, while `gridEditing.tsx` promised it in its own
158
+ comments. `GridCell` now takes `editing: GridEditing | null`; a null session
159
+ means no editor, and nothing else.
160
+ - The tarball canary names `HistoryPanel`, `CommentThread` and
161
+ `FieldHistoryPanel`, so a screen that is green in the monorepo and absent from
162
+ an npm consumer's tarball is caught at the gate.
163
+
164
+
165
+ ## 0.35.0
166
+
167
+ **Per-record AI enrichment: the badge on every agent-owned cell, and the panel behind it.**
168
+
169
+ `EnrichBadge` is drawn beside every value a model owns, always — not on hover and
170
+ not behind a toggle, because a value a machine guessed and a value a person typed
171
+ are different facts and the difference has to survive onto the screen. Hovering it
172
+ gives the rest: who, when, what was read, how sure the model was, what it nearly
173
+ said instead, and the one control that matters — taking the cell back. `stale`
174
+ rides beside it as a second WORD rather than a colour, because "this is six weeks
175
+ old" is a sentence and a hue has to be explained every time.
176
+
177
+ `useEnrichCells` fetches a whole page's provenance in ONE `custom.enrich_cells`,
178
+ and `Grid` uses it for the ids it is actually drawing.
179
+
180
+ `EnrichPanel` is rewritten. It was a per-record "refill these fields" button over a
181
+ port nothing bound; it is now the whole of an enrichment in one screen, in the
182
+ order somebody thinks in: what should go in this column · what it may look at ·
183
+ how often to check it again · what it has cost, per run and per row · SEE IT ON
184
+ FIVE ROWS · and only then, turn it on. Setting one up from a column's own menu
185
+ lands here too, so "Let AI fill" is not a button that leads nowhere.
186
+
187
+ `TableSettings` gains that menu entry beside Edit and Remove, and marks a column a
188
+ model already fills in.
189
+
190
+ **BREAKING:** `EnrichPanel` no longer takes `recordId`. It is a panel about a
191
+ COLUMN and its whole Table, which is what "keep this filled in" is about; the old
192
+ signature could only ever refill one record.
193
+
194
+ The package's own gate now resolves `@ai-matrx/records` to the workspace SOURCE
195
+ rather than the last published tarball — before that, a suite could only exercise
196
+ doors already on npm, so every new door arrived untested through the one gate meant
197
+ to catch it.
198
+
199
+ ## 0.34.0
200
+ ## 0.33.0 — 2026-09-20
201
+
202
+ **"Who changed this?" rendered zero controls for the person most likely to ask
203
+ it.**
204
+
205
+ `gridEditing.tsx` says in its own comments that the cell's history control is
206
+ offered at VIEWER too — "a person who may not edit a cell is exactly the person
207
+ who wants to know who did". `columnForField` did not agree: it chose between the
208
+ cell shell and a plain value on whether there was an EDIT SESSION, and the grid
209
+ passes `null` there whenever the table is not this person's to write. So on a
210
+ viewer, a portal and every read-only embed the shell vanished and took the
211
+ control with it. The shell is now drawn whenever there is an editor OR a
212
+ question to ask, and `GridCell` takes `editing: GridEditing | null` — a null
213
+ session means no editor, and nothing else. `src/who-changed.test.tsx` covers
214
+ it; its load-bearing clause fails on the old gate.
215
+
216
+
217
+ ## 0.32.0
218
+
219
+ **The import wizard, rebuilt on the store's own import doors (SCR-10 / SCR-N-7).**
220
+
221
+ `ImportWizard` parsed a file and then called `recordWrite` **once per row**. At
222
+ the five thousand rows a real spreadsheet holds that is five thousand HTTP round
223
+ trips; there was no duplicate check, so importing a corrected file made a second
224
+ copy of everything; nothing recorded that a value came from a file; and a
225
+ refusal carried a row NUMBER into a file the screen no longer had, so "row 3,184
226
+ was refused" was information nobody could act on. `onProposeField` handed a
227
+ header to a callback and the platform forgot about it.
228
+
229
+ The bytes are still parsed here — Postgres cannot unzip a workbook — and
230
+ **everything after the parse is the store's**:
231
+
232
+ - **The plan, before anything is written.** `importPlan` answers what each
233
+ column is, reading the parity map AND this organization, and the screen shows
234
+ the kind, the store's own sentence saying why, and the first values. Every
235
+ column's destination is a dropdown a person can change, and what they change
236
+ is what the store is sent.
237
+ - **A duplicate key, chosen on the screen** ("same row means the same …"), with
238
+ "leave it alone" or "bring it up to date".
239
+ - **Batches of 250** through `importRows`, with a live count, and **one outcome
240
+ per row**: landed, already here, or refused **with the store's own sentence
241
+ and the row from the person's own file** beside it. The outcome table filters
242
+ to refused / already-here and is the click-through SCR-10 asks for.
243
+ - **The same file twice writes nothing.** The file's sha-256 is the run's
244
+ identity; the screen shows when it ran and what landed, and never sends a row.
245
+ Where Web Crypto is genuinely absent the screen SAYS it cannot recognise the
246
+ file again rather than inventing a hash.
247
+ - **Columns this table does not have go to the ONE approvals inbox** by default,
248
+ and the screen says so on the control itself. "Add them straight away" is
249
+ offered only to an admin and is **absent, not disabled,** for anybody else —
250
+ who are not stuck, because the default still files their column.
251
+ - **One word per type, everywhere.** The wizard reads `FIELD_TYPE_CHOICES`, the
252
+ same list the "Add field" panel offers, so a column is "Money" in both places.
253
+
254
+ `ImportWizardProps.onProposeField` is **gone**: the offer is now a real change
255
+ waiting in a real queue, not a callback the host had to invent a home for. Both
256
+ existing call sites (`TablesHome`, `TablePage`) already pass only `tableId` and
257
+ `onDone`.
258
+
259
+ New in the blocking gate: `src/import-wizard.test.tsx`, five clauses against
260
+ door answers recorded from the live store. Its red twin is `demo/import.red.tsx`
261
+ (the screen as it was), which fails on all three arms.
262
+
263
+ Requires `@ai-matrx/records` >= 0.19.0.
264
+ ## 0.31.0
265
+
266
+ **A record's past is a list of VERSIONS, and an agent names the person it acted for.**
267
+
268
+ `HistoryPanel` listed each field's LATEST value with the author of the last
269
+ write beside it. Useful, and not a history: it could not say what a price was
270
+ before, when it changed, what else changed with it, or put any of it back. It
271
+ reads `custom.record_history` now — one line per VERSION, newest first, each
272
+ naming who, when, the operation in the word somebody used, and every field that
273
+ moved with its before and its after, the Rule version that produced a derived
274
+ value and the evidence behind it. Keyboard-first: Up and Down walk the versions,
275
+ Enter opens one, R starts a restore.
276
+
277
+ **The put-it-back action names what will change before it does.** Pressing
278
+ Restore asks the store what the write would do and shows it — *"Price 1,100 →
279
+ 1,200, Notes cleared"* — and only the second press writes. It says out loud that
280
+ the write is a new version and nothing in the chain is erased, which is the
281
+ thing people are afraid of about an undo button.
282
+
283
+ **`FieldHistoryPanel` is new: "who changed this?", asked of a CELL.** Every grid
284
+ cell now carries the question (a quiet control on hover or focus, and `H` from
285
+ the keyboard at viewer too — a person who may not edit a cell is exactly the
286
+ person who wants to know who did), and the answer is that COLUMN's history
287
+ across the whole Table, with "put this one back" on each row.
288
+
289
+ **`CommentThread` stops keeping its own store.** It declared a package-owned
290
+ system Table called `records_ui_comment` into whichever organization opened the
291
+ screen — the same second store lane FORMS removed for forms and lane DASHBOARDS
292
+ removed for dashboards — and here the rung was wrong in BOTH directions: a
293
+ `commenter` could not comment at all, and anybody with write on the package's
294
+ comment Table could comment on a record they may not open. It reads
295
+ `custom.comment_thread` and writes `custom.comment_write` now, with the people
296
+ NAMED, a comment anchored to one Field, resolve and re-open, and an `@` mention
297
+ picked from the organization's own roster. Whether this person may comment is
298
+ the STORE'S answer, in the same call that answers the thread.
299
+
300
+ **Two defects the SCREENSHOTS found**, neither visible to a test, a type or a
301
+ door: a version's timestamp rendered `9/20/2026, 7:12:37 AM` and wrapped onto two
302
+ lines, doubling the height of every row — it is a relative time now (`12 minutes
303
+ ago`, `Tue 14:12`, then a date) with the exact moment on hover, which is what
304
+ Linear and Notion do and is also what fits. And the cell's "who changed this?"
305
+ control was `display:none` until hover, so it was unreachable by the keyboard
306
+ and invisible to anything driving the screen; it is `opacity-0` now and appears
307
+ on hover OR focus.
308
+
309
+ **Consumer action:** `COMMENT_TABLE` is gone — it described a store that no
310
+ longer exists and nothing outside this package used it. `HistoryPanel` gains an
311
+ optional `onAskAboutField`, `CommentThread` an optional `fieldKey`, and `Grid`,
312
+ `ViewSwitcher` and `columnForField` an optional `onAskWhoChanged`; every one is
313
+ additive and absent means the control is not drawn. Needs
314
+ `@ai-matrx/records` >= 0.18.0.
315
+
3
316
  ## 0.30.0
4
317
 
5
318
  **The notification editor stops asking for the notifier's own reader.**
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