<p>There is a particular kind of quiet that data tools are good at. A filter that silently dropped a condition. An empty result that looks like "no matches" but means "your question was mis-read." An undo that puts back a row that is almost, but not quite, the one you deleted. Nothing throws. Nothing logs. The screen draws a perfectly plausible answer to a question you didn't ask.</p><p>Elyra DataGrid has spent most of its life fighting that quiet, and 0.7.0 is the release where the fight becomes visible on the page. Here's why the grid is built the way it is, how the new release works, and what it looks like in practice.</p><h2>Why: the server owns the truth</h2><p>Most data grids are front-end components. You hand them an array, they sort and filter it in the browser, and the server is a dumb pipe. That's fine until the table has ten million rows, or until a filter the browser applied is one the database would never have allowed.</p><p>DataGrid is server-driven by design. A grid is defined once, in PHP, as a <code>GridDefinition</code>: which columns exist, what type each one is, which are filterable, sortable, editable, which values a column may take. That definition is the allow-list, and everything — a sort, a filter, an edit, a pasted batch of 500 rows, a question typed in plain English — is compiled against it on the server before it touches SQL. The database does the work; the browser draws the result.</p><p>One definition drives three clients — Livewire + Flux UI, Vue 3 + Inertia, Svelte 5 + Inertia — over one protocol. The grid follows your app rather than the other way round. And because the server compiles every filter, it can do things a browser grid cannot: stream a million-row export without holding it in memory, run faceted counts on the real data, group and aggregate with the database's own engine.</p><h2>How: ask the grid, and be told what it heard</h2><p>The feature people remember is <em>ask the grid</em>. Type a question — <code>revenue over 50k in region Nord</code> — and a server-side model turns it into a filter. The trick that makes it safe is that the model is never allowed to write SQL. It is told the grid's own columns and operators, it answers with a filter tree, and that tree is re-validated against the allow-list and compiled exactly like a hand-built filter. A field the grid doesn't declare, an operator a type doesn't support — dropped. Never unsafe SQL.</p><p>Here's the problem 0.7.0 fixes: <em>dropped</em> used to mean <em>silently dropped</em>. The allow-list threw a condition away and the user got a grid answering a narrower question than they asked, with nothing on screen to say so. A perfectly plausible result. The quiet kind.</p><p>Now the same call comes with a report:</p><pre><code class="language-php">$result = $grid-&gt;interpret('revenue over 50k in region Nord, contains "urgent"');

$result-&gt;matched();  // false — something was left out
$result-&gt;dropped();  // [['field' =&gt; 'revenue', 'op' =&gt; 'contains', 'reason' =&gt; 'operator not allowed for number']]
</code></pre><p>All three clients render that under the filter:</p><blockquote><p>We could not apply "contains" to revenue — it's a number.</p></blockquote><p>The user knows exactly what the grid heard, and exactly what it didn't.</p><p>Deliberately, this is not a confidence score. It would have been easy to ask the model "how sure are you?" and render <em>92% confident</em>. But a self-reported confidence isn't calibrated — it's a number that feels like information — and showing it invites the user to trust the filter more at exactly the moment they should check it. What was dropped is ground truth. That's the thing worth rendering.</p><p>Two more wrong answers from the same corner are gone. A question about a text column holding <code>00123</code> used to bind the integer <code>123</code> and match nothing — an empty grid, not an error, the worse failure. And a half-open <code>between</code> compiled to <code>BETWEEN ? AND NULL</code>, which also matched nothing. Values are coerced by the declared type now, and a half-open range is refused and reported.</p><h2>How: explain this view, without asking a model</h2><p>Asking goes one direction: words → filter. 0.7.0 adds the reverse: filter → words.</p><pre><code class="language-php">$grid-&gt;explain();
// "Region is Nord and (revenue over 50 000 or status is Won), matching 'nordic'"
</code></pre><p>That sentence sits under the toolbar in all three clients (<code>public bool $explain = false;</code> turns it off in Livewire). The interesting decision is that it is not an LLM call, even though the other direction is. A filter tree is structured data; describing it is formatting, not inference. So the sentence is faithful by construction — a model asked to describe a filter can describe it wrongly, and the reader can't tell — it costs nothing, needs no API key, and is therefore on every grid rather than only where the assistant is switched on. Nested groups are bracketed, because <code>A and (B or C)</code> and <code>A and B or C</code> select different rows, and that difference is the whole point of writing it down.</p><p>When a question matches nothing, the grid now shows what it <em>can</em> be filtered on: every filterable column, its type, its declared values. That is the allow-list itself — free, and unable to invent a field. Asking the model to suggest a better question would be neither.</p><h2>How: an audit trail that admits what it cannot undo</h2><p>Editing in DataGrid is inline, in-cell, modal or slideover, with batch edits validated on the server. 0.7.0 adds a change event:</p><pre><code class="language-php">use Elyra\DataGrid\Server\Events\GridRowChanged;

Event::listen(GridRowChanged::class, function (GridRowChanged $event) {
    AuditLog::record(
        row:     $event-&gt;key,
        changes: $event-&gt;changes(),   // only what actually differs
        undo:    $event-&gt;inverse(),   // the mutation that would put it back — or null
    );
});
</code></pre><p>Two details carry the design. The event covers only the columns the grid declares — a table's password hashes and tokens stay out of it, the same boundary the mutation response has respected since 0.5.0, when create and update stopped returning <code>SELECT *</code>. An audit trail cannot leak what the grid does not show.</p><p>And <code>inverse()</code> returns <code>null</code> for a delete, on purpose. Restoring the row would go through the write allow-list, which strips the non-editable columns — the key included — so the "restored" row would come back with a new id and missing data. An undo that quietly produces a different row is worse than one that admits it cannot be built. So it admits it.</p><p>Capturing the previous row costs one <code>SELECT</code>, so it happens only when something is listening; a 500-row paste shouldn't pay 500 extra reads for an event with no consumer. A listener that throws is not caught: inside the transactional batch it rolls the write back, which is the right coupling for an audit trail.</p><h2>The part that is really about us</h2><p>The changelog for 0.7.0 opens with a sentence you don't often see: the first release since 0.6.2 in which shipped library code actually changes. 0.6.0, 0.6.1 and 0.6.2 shipped byte-identical package content — tooling, CI and docs work that got a version number because that's what the process did. So there is a release policy now: tag only when something under <code>packages/</code> has changed. A version history should say something.</p><p>The same release deletes an npm publishing setup that pointed at a registry that doesn't exist and never published anything. It was there for a while, looking like a distribution channel. Now the JS packages are marked private, and the one path that works — the clients ship inside the Composer package — is the only one described.</p><p>None of that is a feature. All of it is the reason the features above can be believed.</p><h2>Where to start</h2><p>DataGrid is a Laravel package, licensed per project. Define a grid, point a client at it, and try the thing most grids can't do: ask it a question, and read what it says it left out.</p><p>If a grid that tells you what it heard sounds like the way it should always have worked — the docs at <a target="_blank" rel="noopener noreferrer nofollow" href="https://elyracode.com/docs/datagrid">elyracode.com/docs/datagrid</a> start with <em>why</em>, and the natural-language guide has the new report in it.</p><p>Ask it something. Then read the answer's footnote.</p>