<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rui Wang</title>
    <description>The latest articles on DEV Community by Rui Wang (@jerry_king_d0ee94a9bca82f).</description>
    <link>https://dev.to/jerry_king_d0ee94a9bca82f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4048573%2F96e395b0-f9ea-404b-b1e2-4c98cefe07cf.jpg</url>
      <title>DEV Community: Rui Wang</title>
      <link>https://dev.to/jerry_king_d0ee94a9bca82f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jerry_king_d0ee94a9bca82f"/>
    <language>en</language>
    <item>
      <title>MCP or CLI? Why I Use Both for AI Data Analysis</title>
      <dc:creator>Rui Wang</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:11:02 +0000</pubDate>
      <link>https://dev.to/bigdatasight/mcp-or-cli-why-i-use-both-for-ai-data-analysis-1mmg</link>
      <guid>https://dev.to/bigdatasight/mcp-or-cli-why-i-use-both-for-ai-data-analysis-1mmg</guid>
      <description>&lt;p&gt;When I explore data with an AI assistant, I want to ask follow-up questions, check a calculation, and try another angle. Once I have a useful query, I want to save it and run it again on the next export. MCP and the CLI fit these parts of my workflow well.&lt;/p&gt;

&lt;p&gt;I build BigdataSight, a native Mac data workspace with both interfaces. Here is an example of how they work together. The sample execution records below were created for demonstration.&lt;/p&gt;

&lt;h2&gt;
  
  
  A worked example
&lt;/h2&gt;

&lt;p&gt;Suppose we have execution records from several services and want to know which service had the most failures yesterday. Save these records as &lt;code&gt;runs.csv&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run_id,service,status,finished_at
r1,billing,failed,2026-10-18 09:00:00
r2,billing,succeeded,2026-10-18 10:00:00
r3,billing,failed,2026-10-18 11:00:00
r4,search,succeeded,2026-10-18 12:00:00
r5,search,failed,2026-10-18 13:00:00
r6,ingest,succeeded,2026-10-18 14:00:00
r7,billing,cancelled,2026-10-18 15:00:00
r8,billing,failed,2026-10-19 00:00:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this example, yesterday is October 18, 2026. We count only &lt;code&gt;succeeded&lt;/code&gt; and &lt;code&gt;failed&lt;/code&gt; attempts, treat both the reporting window and timestamps as UTC, and return the failure count and rate, ordered by failure count. Each row is one attempt, including retries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore through MCP
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/docs/learn/server-concepts" rel="noopener noreferrer"&gt;MCP lets an AI client discover tools and contextual resources&lt;/a&gt;. A connected assistant can inspect the file's columns and preview a few rows before writing a query. I would start with this prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use BigdataSight to inspect the runs file I added. Show its schema and a short preview.

For October 18, 2026 in UTC, use finished_at to count succeeded and failed attempts by service. Return finished attempts, failed attempts, and failure percentage, ordered by failure count.

Show the SQL before running it. Ask if the timestamp type or status values need clarification.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking the schema first helps catch details such as a timestamp stored as text or a status field that uses &lt;code&gt;error&lt;/code&gt; for failures. The assistant can then adjust the query to the actual data.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;finished_at&lt;/code&gt; parsed as a UTC timestamp, the query is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;finished_runs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'failed'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;failed_runs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'failed'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;failure_pct&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;runs&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;finished_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-10-18 00:00:00'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;finished_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-10-19 00:00:00'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'succeeded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'failed'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;failed_runs&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the actual table name from your SQL workbench in place of &lt;code&gt;runs&lt;/code&gt;, and adjust timestamp handling to match the column's type. The example returns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;service&lt;/th&gt;
&lt;th&gt;finished_runs&lt;/th&gt;
&lt;th&gt;failed_runs&lt;/th&gt;
&lt;th&gt;failure_pct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;billing&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;66.67&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;search&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;50.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ingest&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Billing has two failures out of three completed attempts. Its cancelled attempt and the failure at midnight on October 19 fall outside our definition. The SQL makes the date range and denominator easy to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save the query as a command
&lt;/h2&gt;

&lt;p&gt;In BigdataSight 1.2, open the source and put the reviewed SQL in the editor. Select the source under &lt;strong&gt;Settings → External Access&lt;/strong&gt; and choose &lt;strong&gt;Structure and data&lt;/strong&gt; access, then use &lt;strong&gt;Run → Copy as Command…&lt;/strong&gt;. The copy window lets you select a shell, result-row limit, and timeout.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;bds&lt;/code&gt; through &lt;strong&gt;Command Line → Set Up bds…&lt;/strong&gt;, select &lt;code&gt;sh&lt;/code&gt; in the copy window, and save the copied command as &lt;code&gt;daily-failures.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh ./daily-failures.sh &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; daily-failures-result.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated command includes the source selection and sends the query to &lt;code&gt;bds query --request -&lt;/code&gt;. The output file contains the JSON query response, subject to the chosen result-row limit. Check the command's exit status before using the result. To keep earlier results, choose a new output filename each time: &lt;code&gt;&amp;gt;&lt;/code&gt; overwrites an existing file.&lt;/p&gt;

&lt;p&gt;Each saved command uses a specific date range and data source. For a reusable script, ask the AI assistant to extract those values into parameters. Review the script and try it on a known date before adding it to your daily workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the interface that fits the work
&lt;/h2&gt;

&lt;p&gt;I like MCP when I am exploring data in a connected AI client: source discovery, query results, and follow-up questions stay in one conversation. A saved CLI command is convenient when I want to repeat a calculation or include it in a script.&lt;/p&gt;

&lt;p&gt;An AI coding assistant with terminal access can also inspect CLI help and run queries directly. And a connected MCP client can repeat a known query. Both support exploration and execution; the surrounding workflow guides my choice. MCP requires client support for the server's tools, while scripts need to handle shell quoting, exit statuses, and JSON output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start a task in one place, retrieve it in another
&lt;/h2&gt;

&lt;p&gt;For a longer query or export, BigdataSight 1.2 Pro supports independent tasks. Ask the AI client to submit a supported operation as a task and keep the returned task ID. You can close that MCP connection and check progress later from the terminal.&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;TASK_ID_FROM_RECEIPT&lt;/code&gt; with the returned ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s1"&gt;'{"version":1,"jobID":"TASK_ID_FROM_RECEIPT"}'&lt;/span&gt; |
  bds &lt;span class="nb"&gt;jobs &lt;/span&gt;get &lt;span class="nt"&gt;--request&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the task completes, retrieve its result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s1"&gt;'{"version":1,"jobID":"TASK_ID_FROM_RECEIPT"}'&lt;/span&gt; |
  bds &lt;span class="nb"&gt;jobs &lt;/span&gt;result &lt;span class="nt"&gt;--request&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check this handoff, I supplied the same eight records directly in SQL, submitted the query through MCP, and closed the connection. CLI and a new MCP session both retrieved the same three summary rows.&lt;/p&gt;

&lt;p&gt;Running the saved query calculates a fresh result from the selected source. Retrieving a completed task gives you the result captured by that run. If the source changes, you can keep the earlier result for comparison.&lt;/p&gt;

&lt;p&gt;Keep the Mac awake while a task runs. Tasks expire 24 hours after submission, and retrieval requires the relevant source access and Pro entitlement. Save results you want to keep before they expire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using this workflow
&lt;/h2&gt;

&lt;p&gt;For a calculation I plan to reuse, I keep the SQL, source selection, dates, timezone, status definitions, and result-row limit alongside the output. That gives me what I need to review or adjust the next run.&lt;/p&gt;

&lt;p&gt;Queries run on the Mac. Schemas, previews, and results returned to an AI client may be sent to its model provider; check the client's data settings when choosing what to share.&lt;/p&gt;

&lt;p&gt;You can try local CSV and Parquet queries with BigdataSight's free Viewer. Pro adds more formats, remote sources, comparisons, broader exports, and independent tasks. The steps here use &lt;strong&gt;BigdataSight 1.2&lt;/strong&gt;. See the &lt;a href="https://bigdatasight.com/agent-gateway" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;Where would a saved command help most in your AI data analysis workflow?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: We build BigdataSight, a native Mac data workstation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-assistance disclosure: I used an AI coding assistant to help research and edit this article. I reviewed the final argument and the linked specifications before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>cli</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>How AI can investigate a 12 GB export without reading it into a prompt</title>
      <dc:creator>Rui Wang</dc:creator>
      <pubDate>Mon, 05 Oct 2026 04:27:12 +0000</pubDate>
      <link>https://dev.to/bigdatasight/how-ai-can-investigate-a-12-gb-export-without-reading-it-into-a-prompt-3plf</link>
      <guid>https://dev.to/bigdatasight/how-ai-can-investigate-a-12-gb-export-without-reading-it-into-a-prompt-3plf</guid>
      <description>&lt;p&gt;Imagine asking an AI assistant for the average order value in a 12 GB sales export. It finds an amount column and suggests &lt;code&gt;AVG(line_amount)&lt;/code&gt;. The query runs. The number looks reasonable. It answers the wrong business question.&lt;/p&gt;

&lt;p&gt;The file has one row per order line, not one row per order. The difficult part was deciding what a row meant before choosing the calculation.&lt;/p&gt;

&lt;p&gt;This is a useful place to start when connecting AI to local data. A model can help formulate questions and write queries. A data engine can execute those queries over millions of rows. The engineer supplies the business definition that neither the column names nor the SQL syntax can settle.&lt;/p&gt;

&lt;p&gt;The example below uses four invented order lines to explain that division of work. The 12 GB file is a hypothetical setting, not a performance benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the grain before choosing an aggregate
&lt;/h2&gt;

&lt;p&gt;Suppose the export contains these lines:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;line_amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;100.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;30.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;40.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;30.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both orders total 100.00. Average order value is therefore 100.00. But the average line amount is &lt;code&gt;(100 + 30 + 40 + 30) / 4 = 50.00&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Giving the assistant more rows would not fix that calculation. It needs to know the grain: what entity one row represents. A column named &lt;code&gt;amount&lt;/code&gt; might hold a line amount, a repeated order total, or a payment amount. Each requires a different query.&lt;/p&gt;

&lt;p&gt;For this example, the engineer defines the metric as follows: sum the sales lines belonging to each order, then average those order totals. Use USD orders placed in October 2026, with timestamps interpreted in UTC. Assume each order has one currency and the export contains sales lines; refunds are recorded separately. These choices make the calculation concrete.&lt;/p&gt;

&lt;p&gt;Schema inspection supplies the available fields and types. A short preview helps distinguish a line amount from a repeated order total. The next step is to express the chosen grain in SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;per_order&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;line_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;order_amount&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ordered_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-10-01 00:00:00'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;ordered_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;  &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-11-01 00:00:00'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'USD'&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;order_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;line_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;sales_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;average_order_value&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;per_order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;orders&lt;/code&gt; stands for a relation opened in a SQL workbench. The query assumes &lt;code&gt;ordered_at&lt;/code&gt; is a UTC timestamp without a timezone and &lt;code&gt;line_amount&lt;/code&gt; is a non-null decimal sales amount. With a timezone-aware timestamp, use literals or parameters appropriate to the engine and the business timezone.&lt;/p&gt;

&lt;p&gt;The inner aggregation changes the grain from line to order. The outer one computes the business metric at that new grain. On the four-line example, the result is two orders, four lines, 200.00 in sales, and an average order value of 100.00.&lt;/p&gt;

&lt;p&gt;Keeping both counts in the result makes the calculation easier to discuss. If the assistant suggests averaging &lt;code&gt;sales_amount / line_count&lt;/code&gt;, the engineer can point to the denominator immediately. The useful conversation is about orders and lines, rather than a long pasted table.&lt;/p&gt;

&lt;h2&gt;
  
  
  One result row can still require a large scan
&lt;/h2&gt;

&lt;p&gt;That query returns one row. Computing it may require processing every qualifying sales line. Result size and execution cost are different dimensions.&lt;/p&gt;

&lt;p&gt;Adding &lt;code&gt;LIMIT 1&lt;/code&gt; to an aggregate does not generally reduce the rows fed into it. The aggregate already produces one row. Moving a limit into an input subquery reduces the input, but also changes the average being calculated.&lt;/p&gt;

&lt;p&gt;For large local files, the better question is which columns and file regions the engine must read. The SQL above references four fields: &lt;code&gt;order_id&lt;/code&gt;, &lt;code&gt;line_amount&lt;/code&gt;, &lt;code&gt;ordered_at&lt;/code&gt;, and &lt;code&gt;currency&lt;/code&gt;. An export might contain 180 fields, including descriptions, addresses, and serialized attributes that this query never uses.&lt;/p&gt;

&lt;p&gt;Parquet stores data in column chunks within row groups. Its footer locates those chunks, so a reader can find the requested columns without decoding all the others. The &lt;a href="https://parquet.apache.org/docs/file-format/" rel="noopener noreferrer"&gt;Parquet file layout&lt;/a&gt; explains how readers start from file metadata to locate column data.&lt;/p&gt;

&lt;p&gt;This is projection pushdown: carrying the query's column selection down to the file reader. Four fields instead of 180 can materially reduce the work, although the saving depends on their encoded sizes. A large text field and a small integer field do not cost the same amount to read.&lt;/p&gt;

&lt;p&gt;The date filter can reduce the work further. Suppose the file has these row-group statistics for &lt;code&gt;ordered_at&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Row group&lt;/th&gt;
&lt;th&gt;Minimum date&lt;/th&gt;
&lt;th&gt;Maximum date&lt;/th&gt;
&lt;th&gt;October query&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;September 1&lt;/td&gt;
&lt;td&gt;September 15&lt;/td&gt;
&lt;td&gt;Skip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;September 16&lt;/td&gt;
&lt;td&gt;October 8&lt;/td&gt;
&lt;td&gt;Read relevant columns and filter rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;November 1&lt;/td&gt;
&lt;td&gt;November 14&lt;/td&gt;
&lt;td&gt;Skip&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With usable min/max statistics, groups 0 and 2 cannot contain an October order. Group 1 overlaps the requested interval, so its relevant data still needs to be examined. Readers such as DuckDB use this form of &lt;a href="https://duckdb.org/docs/current/data/parquet/overview#partial-reading" rel="noopener noreferrer"&gt;filter pushdown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now shuffle the same dates across every row group. Each group's minimum may be September 1 and maximum November 14. The filter has not changed, but the reader loses those opportunities to skip groups.&lt;/p&gt;

&lt;p&gt;That is why file layout matters even when the assistant writes good SQL. Clustering rows by a frequently filtered field can make its statistics more selective. Smaller row groups offer finer skipping, while very small groups add metadata and scheduling overhead. DuckDB's &lt;a href="https://duckdb.org/docs/current/guides/performance/file_formats" rel="noopener noreferrer"&gt;file-format performance guidance&lt;/a&gt; discusses that tradeoff. There is no single row-group size that makes every workload fast.&lt;/p&gt;

&lt;p&gt;CSV has a different cost model: readers commonly infer its types from text, and a plain CSV file has no Parquet-style row-group statistics for a date filter to skip unrelated regions. An interface that looks identical can therefore do quite different amounts of reading underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  A query result is a useful compression of the question
&lt;/h2&gt;

&lt;p&gt;A raw preview preserves individual values. An aggregation preserves a relationship chosen by the question: sales per order, errors per service, bytes per partition, or missing values per column.&lt;/p&gt;

&lt;p&gt;Consider what happens after the first average is computed. The engineer wants to know whether orders with many lines have a different average value. Reuse the &lt;code&gt;per_order&lt;/code&gt; calculation and replace its final &lt;code&gt;SELECT&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;line_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;order_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;average_order_value&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;per_order&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;line_count&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;line_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the teaching example, the output has just two groups:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lines per order&lt;/th&gt;
&lt;th&gt;Orders&lt;/th&gt;
&lt;th&gt;Average order value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The assistant can now discuss a specific relationship without reading all four lines, let alone the full export. On real data, the engineer might next ask for a comparison by sales channel or a breakdown of unusually large orders. The result suggests the next calculation.&lt;/p&gt;

&lt;p&gt;This compression is task-dependent. Grouping by a low-cardinality status usually produces a short result. Grouping by a nearly unique &lt;code&gt;order_id&lt;/code&gt; may produce millions of rows. &lt;code&gt;GROUP BY&lt;/code&gt; alone does not make an answer compact; the chosen dimension does.&lt;/p&gt;

&lt;p&gt;It also matters how partial results are combined. Suppose two channels report average order values of 100 and 20, based on 10 and 90 orders respectively. The overall average is &lt;code&gt;(100 × 10 + 20 × 90) / 100 = 28&lt;/code&gt;, not 60. Passing the counts alongside the averages lets the assistant combine them correctly. Passing only two averages throws away the weights.&lt;/p&gt;

&lt;p&gt;For exact recombination, prefer carrying the underlying sum and count instead of rounded averages. The same idea appears in distributed aggregation:&lt;br&gt;
&lt;code&gt;SUM&lt;/code&gt; and &lt;code&gt;COUNT&lt;/code&gt; can be combined across partitions, while &lt;code&gt;AVG&lt;/code&gt; needs both.&lt;br&gt;
Choosing what to return is part of the computation, not just formatting a response for the model.&lt;/p&gt;
&lt;h2&gt;
  
  
  Give the assistant the definition it cannot infer
&lt;/h2&gt;

&lt;p&gt;“Analyze this file” leaves both the metric and the reading strategy open.&lt;br&gt;
A more productive starting request is concrete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This export has one row per sales line. I need average order value
for USD orders placed in October, using UTC dates. Sum line_amount
per order_id before averaging. Start by inspecting the schema and
checking that these fields have the expected meaning. Suggest the
SQL, including order and line counts, for me to review and run.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant can map that request to the actual column names, draft the query, explain the grouping, and suggest a useful next breakdown. The engineer decides what counts as a sale and whether the proposed calculation matches how the business measures it. The local engine does the arithmetic.&lt;/p&gt;

&lt;p&gt;In BigdataSight Direct 1.1, the Agent connection lets an AI client inspect local file structure and, when enabled, short value previews. We supports Parquet, CSV, TSV, JSON/NDJSON/JSONL, SQLite, Excel, and Arrow/Feather, including table, view, or worksheet selection where needed.&lt;/p&gt;

&lt;p&gt;For the Parquet/CSV example above, inspect the file through Agent, review and run the proposed SQL in the app's SQL workbench, then bring the small query result back into the conversation. The Agent inspection step and the SQL workbench step are separate parts of that workflow. Connection instructions are in the &lt;a href="https://bigdatasight.com/agent-gateway" rel="noopener noreferrer"&gt;Agent setup guide&lt;/a&gt;; channel availability is recorded in the &lt;a href="https://bigdatasight.com/changelog" rel="noopener noreferrer"&gt;release notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next time an assistant proposes a number, ask it to show the grouping and the denominator. That short exchange often contributes more than another thousand rows of context.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: We build BigdataSight, a native Mac data workstation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-assistance disclosure: I used an AI coding assistant to help research and edit this article. I reviewed the final argument and the linked specifications before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>sql</category>
      <category>ai</category>
      <category>parquet</category>
    </item>
    <item>
      <title>The AI agent checked the table. Did every result describe the same state?</title>
      <dc:creator>Rui Wang</dc:creator>
      <pubDate>Tue, 25 Aug 2026 07:29:28 +0000</pubDate>
      <link>https://dev.to/bigdatasight/the-ai-agent-checked-the-table-did-every-result-describe-the-same-state-3gpo</link>
      <guid>https://dev.to/bigdatasight/the-ai-agent-checked-the-table-did-every-result-describe-the-same-state-3gpo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Why agent data tools need exact table identity, bounded coverage, and honest incomplete results beyond a successful tool call.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An AI agent reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table state: Delta version 1183
Schema check: Pass
Active files: 247 present
Release rule: Pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool calls completed without an error. Can another agent rely on this result?&lt;/p&gt;

&lt;p&gt;Suppose the table advanced from version 1182 to 1183 while the checks were running. The timeline call observed version 1183. The schema call used metadata cached from version 1182. The file check resolved the current table but did not return a version at all.&lt;/p&gt;

&lt;p&gt;Every line may be accurate on its own. Together, they describe no real table state.&lt;/p&gt;

&lt;p&gt;The values in this example are fictional. The failure shape is not. A mutable dataset can change between calls, retries, cache reads, and handoffs between agents. Once several valid observations are combined, the result can become a claim that none of the tools actually proved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transport is not evidence semantics
&lt;/h2&gt;

&lt;p&gt;The July 2026 release of the &lt;a href="https://blog.modelcontextprotocol.io/posts/2026-07-28/" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;&lt;br&gt;
made its core stateless and each request self-describing. MCP gives clients and servers a common way to discover and call tools. The &lt;a href="https://github.com/a2aproject/A2A/blob/main/docs/specification.md" rel="noopener noreferrer"&gt;Agent2Agent protocol&lt;/a&gt;&lt;br&gt;
defines communication between independent agent systems.&lt;/p&gt;

&lt;p&gt;These protocols solve real connection problems. They do not define the domain meaning of every tool result.&lt;/p&gt;

&lt;p&gt;An MCP tool can return a valid JSON object called &lt;code&gt;schema_check&lt;/code&gt;. An A2A agent can send that object to another agent. Neither fact tells the receiver whether the schema and file checks observed the same table state, whether a read limit stopped the inspection, or whether an unsupported delete feature was ignored.&lt;/p&gt;

&lt;p&gt;Protocol success answers, "Did the message arrive and satisfy the wire contract?" Data evidence answers, "What can this result prove?"&lt;/p&gt;
&lt;h2&gt;
  
  
  Data formats already give us stable states
&lt;/h2&gt;

&lt;p&gt;The data layer has useful identities that an evidence contract can reuse.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/delta-io/delta/blob/master/PROTOCOL.md" rel="noopener noreferrer"&gt;Delta Transaction Log Protocol&lt;/a&gt; defines a serial history of atomic table versions. A snapshot at a version contains the table metadata and the set of files that belong to that state.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://iceberg.apache.org/spec/" rel="noopener noreferrer"&gt;Apache Iceberg specification&lt;/a&gt;&lt;br&gt;
defines snapshots, manifest lists, manifests, data files, and delete files. A snapshot identifies the table state used to plan a scan.&lt;/p&gt;

&lt;p&gt;Those identities are stronger than &lt;code&gt;latest&lt;/code&gt;, a wall-clock time, or an object storage prefix. They let an agent say which state it observed even after the table changes again.&lt;/p&gt;

&lt;p&gt;Other data sources have equivalent anchors: an object version, a content digest, a database transaction identifier, or a catalog reference resolved to an immutable state. A useful agent contract should preserve the native identity instead of inventing a weaker one.&lt;/p&gt;
&lt;h2&gt;
  
  
  The surrounding standards cover different parts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openlineage.io/docs/spec/object-model/" rel="noopener noreferrer"&gt;OpenLineage&lt;/a&gt; models jobs, runs, and datasets. Its dataset facets can carry a dataset version, while its &lt;a href="https://openlineage.io/docs/spec/facets/dataset-facets/data_quality_assertions/" rel="noopener noreferrer"&gt;data quality assertions facet&lt;/a&gt; records test outcomes and optional expected and observed values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.w3.org/TR/prov-overview/" rel="noopener noreferrer"&gt;W3C PROV&lt;/a&gt; provides a general model for entities, activities, agents, and derivations. It is a foundation for describing where an artifact came from and which activity produced it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-agent-spans.md" rel="noopener noreferrer"&gt;OpenTelemetry semantic conventions for GenAI agents&lt;/a&gt; are developing shared names for agent invocations, plans, workflows, and tool execution spans. That work helps operators observe how an agent ran.&lt;/p&gt;

&lt;p&gt;Newer proposals are also exploring cryptographically verifiable agent&lt;br&gt;
evidence. For example,&lt;br&gt;
&lt;a href="https://proofofinsight.org/" rel="noopener noreferrer"&gt;Proof of Insight&lt;/a&gt; describes a content-addressed, signed graph for observation, deterministic computation, model reasoning, and attestation. Its current specification labels itself a working draft rather than a published standard.&lt;/p&gt;

&lt;p&gt;These specifications overlap, but they do not require the same data-evidence boundary.&lt;/p&gt;

&lt;p&gt;In the specifications above, I have not found a widely adopted profile that requires a data-check result to bind all of the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Subject and state&lt;/td&gt;
&lt;td&gt;Which exact dataset state was observed?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Question or rule&lt;/td&gt;
&lt;td&gt;What was the check trying to establish?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope and coverage&lt;/td&gt;
&lt;td&gt;Which metadata, files, partitions, samples, or rows were read?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capability&lt;/td&gt;
&lt;td&gt;Which table features and delete semantics did the reader understand?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outcome&lt;/td&gt;
&lt;td&gt;Did the rule pass, warn, fail, or remain incomplete?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Limitations and provenance&lt;/td&gt;
&lt;td&gt;What stopped the check, and how can another system reproduce or challenge it?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;OpenLineage can represent several of these facts. PROV can express their provenance. MCP or A2A can carry them. The missing piece is a small semantic profile that says which facts are required for an agent-facing data verification result and how they remain bound to one observation.&lt;/p&gt;
&lt;h2&gt;
  
  
  A profile is a better starting point than a new transport
&lt;/h2&gt;

&lt;p&gt;Creating another RPC or messaging protocol would duplicate work. The same evidence should be able to travel through MCP, A2A, a command-line process, an event stream, or a saved file.&lt;/p&gt;

&lt;p&gt;The standardizable part is the meaning of the result.&lt;/p&gt;

&lt;p&gt;An illustrative envelope might express this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;delta&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1183&lt;/span&gt;
&lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;schema_compatibility&lt;/span&gt;
&lt;span class="na"&gt;coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;complete&lt;/span&gt;
  &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;not_read&lt;/span&gt;
&lt;span class="na"&gt;outcome&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;incomplete&lt;/span&gt;
&lt;span class="na"&gt;limitations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;active_file_check_observed_a_different_state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a proposed wire schema. It only shows the separation between table identity, the question, observed coverage, outcome, and limitation.&lt;/p&gt;

&lt;p&gt;A practical profile would need stronger rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A mutable source must resolve to an exact state, or the result remains incomplete.&lt;/li&gt;
&lt;li&gt;Observations from incompatible states cannot be merged under one verdict.&lt;/li&gt;
&lt;li&gt;Transport completion and evidence outcome use separate status fields.&lt;/li&gt;
&lt;li&gt;Successful sections remain available when another section is incomplete.&lt;/li&gt;
&lt;li&gt;Unsupported table features and exhausted read limits appear in the result.&lt;/li&gt;
&lt;li&gt;The shareable form excludes credentials, signed URLs, private locations, raw rows, and hidden model reasoning.&lt;/li&gt;
&lt;li&gt;A policy decision remains separate from the observations it evaluates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The seventh rule matters when an agent participates in a release workflow. A schema comparison can prove that one field changed. It cannot decide whether the change is allowed until it receives an explicit compatibility or release rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bounded reads need an honest result
&lt;/h2&gt;

&lt;p&gt;Agents make repeated work cheap to request. They do not make remote metadata, object listings, or full scans free.&lt;/p&gt;

&lt;p&gt;A data tool needs budgets for time, bytes, objects, metadata entries, rows, and retries. When a budget ends the operation, the result should preserve what was observed and mark the unsupported conclusion as &lt;code&gt;Incomplete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Retrying silently can make the evidence worse. The next request may observe a new table state and fill the missing section with facts from a different snapshot. A clean-looking report then hides the boundary that mattered most.&lt;/p&gt;

&lt;p&gt;An incomplete result can still be useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;State identity: Verified at Iceberg snapshot 711
Schema check: Pass over snapshot metadata
Delete check: Incomplete after the manifest-read limit
Row impact: Not checked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another agent can reuse the state and schema evidence, request a larger delete budget with approval, or send the result to a person. It does not need to discard the whole operation or pretend the missing premise passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A standard should earn its name
&lt;/h2&gt;

&lt;p&gt;Publishing a JSON Schema is easy. Establishing a useful standard requires independent use.&lt;/p&gt;

&lt;p&gt;A credible path would start with one narrow profile for read-only observations of versioned analytical data. It would include mappings to MCP tool results, A2A artifacts, OpenLineage facets, and PROV records. A public conformance set would cover state changes during inspection, unsupported delete semantics, bounded reads, redaction, and incompatible evidence binding.&lt;/p&gt;

&lt;p&gt;The profile would become more convincing when two unrelated implementations can exchange the same result and reject the same invalid combinations. Open governance, versioning rules, and a compatibility policy should follow before anyone calls it an industry standard.&lt;/p&gt;

&lt;p&gt;That suggests a modest first step: publish a data-evidence profile draft, its mappings, and failure cases. Ask data tool builders, lineage projects, and agent framework authors where it conflicts with their existing models. Change the draft before giving it a brand.&lt;/p&gt;

&lt;h2&gt;
  
  
  A BigdataSight development note
&lt;/h2&gt;

&lt;p&gt;In our post-1.0 research, we are exploring how local agents can request bounded, read-only checks of Delta and Iceberg tables and receive compact evidence without source locations, credentials, schema bodies, or row values.&lt;/p&gt;

&lt;p&gt;The recurring constraint is that observations must stay attached to the state that produced them. Speed matters only when the observations describe the same state. Interoperability matters only when the next agent can see their coverage and limitations.&lt;/p&gt;

&lt;p&gt;We are treating MCP and other transports as adapters around stable data semantics. Product truth stays in the data contract. The broader idea of an open data-evidence profile is research. It is not a shipping BigdataSight 1.0 capability, a published specification, or a delivery commitment.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question for data and agent engineers
&lt;/h2&gt;

&lt;p&gt;If an AI agent returns a data verification result, which missing fact makes the result hardest to trust?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The exact table version, snapshot, or object identity&lt;/li&gt;
&lt;li&gt;The metadata, files, partitions, samples, or rows it covered&lt;/li&gt;
&lt;li&gt;Unsupported features or delete semantics&lt;/li&gt;
&lt;li&gt;The expectation or release rule used for the verdict&lt;/li&gt;
&lt;li&gt;A reproducible provenance or evidence reference&lt;/li&gt;
&lt;li&gt;Something else&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A one-number comment is enough, and important to us.😊&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: We build BigdataSight, a native Mac data workstation. This article describes a post-1.0 research direction, not a shipping 1.0 capability, public protocol, or release commitment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-assistance disclosure: I used an AI coding assistant to help research and edit this article. I reviewed the final argument and the linked specifications before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dataengineering</category>
      <category>mcp</category>
      <category>lakehouse</category>
    </item>
    <item>
      <title>The report says verified. Is the change safe to release?</title>
      <dc:creator>Rui Wang</dc:creator>
      <pubDate>Wed, 12 Aug 2026 11:30:26 +0000</pubDate>
      <link>https://dev.to/bigdatasight/the-report-says-verified-is-the-change-safe-to-release-4g96</link>
      <guid>https://dev.to/bigdatasight/the-report-says-verified-is-the-change-safe-to-release-4g96</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How to keep table identity, evidence coverage, and release intent separate in a data investigation report.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A schema-change report contains four lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Previous state: Delta version 41
Current state: Delta version 42
Schema changes: Verified
Fields added: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The feature request expected one new nullable &lt;code&gt;double&lt;/code&gt; field named &lt;code&gt;score&lt;/code&gt;.&lt;br&gt;
Can version 42 go to production?&lt;/p&gt;

&lt;p&gt;Not from those four lines.&lt;/p&gt;

&lt;p&gt;The report established an observed change between two table states. It did not&lt;br&gt;
establish that the added field is &lt;code&gt;score&lt;/code&gt;, that its type is &lt;code&gt;double&lt;/code&gt;, or that&lt;br&gt;
the change matches the feature request. &lt;code&gt;Verified&lt;/code&gt; describes the evidence&lt;br&gt;
check. It is not a synonym for &lt;code&gt;safe to release&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The numbers in this article are fictional, but this gap is common whenever a&lt;br&gt;
data report mixes observation and approval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two questions, two different answers
&lt;/h2&gt;

&lt;p&gt;A data investigation usually starts with a factual question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What changed between the two states?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A release review asks a second question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did the observed change match what we intended and what consumers allow?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first answer comes from evidence. The second also needs an expectation or&lt;br&gt;
rule.&lt;/p&gt;

&lt;p&gt;Suppose a metadata comparison confirms that version 42 contains one more field&lt;br&gt;
than version 41. That is useful. It narrows the investigation. It still cannot&lt;br&gt;
approve the release because several different changes produce the same count:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;score&lt;/code&gt; was added as nullable &lt;code&gt;double&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;score&lt;/code&gt; was added as nullable &lt;code&gt;string&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;an unrelated field was added;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;score&lt;/code&gt; was added, but an existing field also changed type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All four cases can display &lt;code&gt;Fields added: 1&lt;/code&gt;. Only the first matches the stated&lt;br&gt;
request.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a useful report must preserve
&lt;/h2&gt;

&lt;p&gt;The report needs enough context to stop a reader from silently strengthening&lt;br&gt;
its conclusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  The exact before and after
&lt;/h3&gt;

&lt;p&gt;“Previous” and “current” are unstable labels. Record Delta versions, Iceberg&lt;br&gt;
snapshot IDs, object versions, or another durable identity.&lt;/p&gt;

&lt;p&gt;If the table advances to version 43 five minutes later, another engineer should&lt;br&gt;
still be able to inspect the 41-to-42 edge. Otherwise, the rerun answers a new&lt;br&gt;
question while looking like a reproduction of the old one.&lt;/p&gt;

&lt;h3&gt;
  
  
  What was actually read
&lt;/h3&gt;

&lt;p&gt;Metadata can support schema, snapshot, manifest, and recorded file-count&lt;br&gt;
claims. It cannot prove a claim about every row.&lt;/p&gt;

&lt;p&gt;A bounded sample can reveal a suspicious pattern. It cannot prove that the&lt;br&gt;
pattern is absent outside the sample. A full scan supports stronger row-level&lt;br&gt;
claims only when it covered every relevant partition and applied the table's&lt;br&gt;
delete semantics.&lt;/p&gt;

&lt;p&gt;Write the coverage beside the result. Do not hide it in a log or tooltip.&lt;/p&gt;

&lt;h3&gt;
  
  
  Independent results
&lt;/h3&gt;

&lt;p&gt;One missing check should not erase evidence that was successfully collected.&lt;br&gt;
It should also not disappear behind one green report status.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State identity&lt;/td&gt;
&lt;td&gt;Verified&lt;/td&gt;
&lt;td&gt;Versions 41 and 42 were resolved.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema summary&lt;/td&gt;
&lt;td&gt;Verified&lt;/td&gt;
&lt;td&gt;One field was added.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field detail&lt;/td&gt;
&lt;td&gt;Incomplete&lt;/td&gt;
&lt;td&gt;The shareable report does not include the field path or type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row impact&lt;/td&gt;
&lt;td&gt;Not checked&lt;/td&gt;
&lt;td&gt;No table rows were read.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This report is more useful than either “passed” or “failed.” It tells the next&lt;br&gt;
reviewer what can be reused and what still needs work.&lt;/p&gt;

&lt;h3&gt;
  
  
  The limitation and the next check
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Incomplete&lt;/code&gt; is not an error message. It is a result with a missing premise.&lt;/p&gt;

&lt;p&gt;In this example, the next check is small: compare the added field's path, type,&lt;br&gt;
and nullability with the feature request. If a local detailed review observes&lt;br&gt;
&lt;code&gt;score&lt;/code&gt; as nullable &lt;code&gt;string&lt;/code&gt;, the release rule fails. If the detail is not&lt;br&gt;
available, the decision remains incomplete. Neither case should become a pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters to automation
&lt;/h2&gt;

&lt;p&gt;A person may notice that &lt;code&gt;Verified&lt;/code&gt; and &lt;code&gt;Fields added: 1&lt;/code&gt; do not answer the&lt;br&gt;
release question. An automated step can make a stronger assumption because the&lt;br&gt;
word looks conclusive.&lt;/p&gt;

&lt;p&gt;Giving the same report to an AI agent does not repair missing evidence. The&lt;br&gt;
machine-readable version must preserve the same state identities, coverage,&lt;br&gt;
independent results, and limitations that a person sees. It should not turn an&lt;br&gt;
absent field-level check into approval.&lt;/p&gt;

&lt;p&gt;This does not require a giant document. It requires stable meaning. A compact&lt;br&gt;
structured report can say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which two states were compared;&lt;/li&gt;
&lt;li&gt;which observations completed;&lt;/li&gt;
&lt;li&gt;what each observation covered;&lt;/li&gt;
&lt;li&gt;what remains incomplete;&lt;/li&gt;
&lt;li&gt;which rule, if any, produced a decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent can then combine the report with project context it already has. If&lt;br&gt;
the report contains evidence but no release rule, the agent should describe&lt;br&gt;
the evidence and leave the release decision open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five questions before trusting a report
&lt;/h2&gt;

&lt;p&gt;Before accepting a green status, ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which exact before-and-after states were compared?&lt;/li&gt;
&lt;li&gt;What metadata, files, samples, or rows were actually read?&lt;/li&gt;
&lt;li&gt;Does &lt;code&gt;Verified&lt;/code&gt; describe an observation or a decision against an explicit rule?&lt;/li&gt;
&lt;li&gt;What missing evidence would make the result incomplete?&lt;/li&gt;
&lt;li&gt;Can another person repeat the same check after the table changes again?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the report cannot answer one of them, narrow the claim. “One field was added&lt;br&gt;
between versions 41 and 42” may be fully supported. “Version 42 is safe to&lt;br&gt;
release” is a different claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  A BigdataSight development note
&lt;/h2&gt;

&lt;p&gt;In a post-1.0 development build, BigdataSight can generate a read-only&lt;br&gt;
Investigation Report for an opened remote Delta or Iceberg table. The report&lt;br&gt;
brings current-to-previous state checks into one view, keeps section results&lt;br&gt;
independent, and can copy a bounded report without source locations,&lt;br&gt;
credentials, schema bodies, or row values.&lt;/p&gt;

&lt;p&gt;The user starts the report explicitly. A completed report does not certify that&lt;br&gt;
the table is correct, compatible, or safe to release. When a bounded check&lt;br&gt;
cannot establish a result, the section remains &lt;code&gt;Incomplete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This work has passed automated and hands-on development acceptance. It is not&lt;br&gt;
part of the current App Store 1.0 release, and this note does not promise a&lt;br&gt;
release date.&lt;/p&gt;

&lt;p&gt;Our broader direction is evidence-backed, local-first data investigation and&lt;br&gt;
reproducible verification, read-only by default. The useful outcome is not a&lt;br&gt;
larger green badge. It is a report whose conclusion cannot outrun its evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question for data engineers
&lt;/h2&gt;

&lt;p&gt;When you review a data change, which detail is most often missing from the&lt;br&gt;
report?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The exact before-and-after states&lt;/li&gt;
&lt;li&gt;What data or metadata was checked&lt;/li&gt;
&lt;li&gt;The expected change or release rule&lt;/li&gt;
&lt;li&gt;The reason a result is incomplete&lt;/li&gt;
&lt;li&gt;Reproduction steps&lt;/li&gt;
&lt;li&gt;Something else&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A one-number comment is enough. Please do not post confidential data, file&lt;br&gt;
paths, SQL, schemas, credentials, customer information, or screenshots.&lt;/p&gt;

&lt;p&gt;If you prefer email, use the one-question prompt on the&lt;br&gt;
&lt;a href="https://bigdatasight.com/research" rel="noopener noreferrer"&gt;BigdataSight research page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: We build BigdataSight, a native Mac data workstation. This article describes a post-1.0 development artifact and its design boundary, not a shipping 1.0 capability or delivery commitment.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Writing note: I developed and verified the technical argument in this article. AI tools assisted with English drafting and editing.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dataengineering</category>
      <category>database</category>
      <category>testing</category>
      <category>agents</category>
    </item>
    <item>
      <title>The query succeeded. Which table state did it read?</title>
      <dc:creator>Rui Wang</dc:creator>
      <pubDate>Thu, 30 Jul 2026 02:31:28 +0000</pubDate>
      <link>https://dev.to/bigdatasight/the-query-succeeded-which-table-state-did-it-read-1g7o</link>
      <guid>https://dev.to/bigdatasight/the-query-succeeded-which-table-state-did-it-read-1g7o</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Why two successful queries can count different rows when they resolve different Delta or Iceberg table states.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two queries return different counts for the same Iceberg table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the catalog query returns 12,480,032 rows;&lt;/li&gt;
&lt;li&gt;a direct scan of the storage folder returns 12,691,404 rows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither query fails. The difference is 211,372 rows. The numbers are&lt;br&gt;
fictional, but the investigation problem is real.&lt;/p&gt;

&lt;p&gt;Before comparing SQL engines, compare the inputs. The catalog query and the&lt;br&gt;
folder scan may not have read the same logical table.&lt;/p&gt;

&lt;h2&gt;
  
  
  A lakehouse table is a selection of files
&lt;/h2&gt;

&lt;p&gt;A Delta or Iceberg table may store its rows in Parquet files, but the directory&lt;br&gt;
is not the table definition.&lt;/p&gt;

&lt;p&gt;For Delta, a table version is reconstructed from its transaction log. The log&lt;br&gt;
records which logical files were added or removed. A logical file can also&lt;br&gt;
carry a deletion vector that marks rows as no longer present. A file removed&lt;br&gt;
from the current version may remain in storage until a later cleanup.&lt;/p&gt;

&lt;p&gt;For Iceberg, a catalog or table reference points to table metadata. That&lt;br&gt;
metadata identifies a snapshot. The snapshot points to a manifest list, and&lt;br&gt;
the manifests identify the data files and delete files used for the scan.&lt;/p&gt;

&lt;p&gt;This distinction explains why listing every Parquet object can produce a&lt;br&gt;
plausible but incorrect result. The folder may contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;files used by the selected table state;&lt;/li&gt;
&lt;li&gt;files retained for an older state;&lt;/li&gt;
&lt;li&gt;files left by a failed or abandoned write;&lt;/li&gt;
&lt;li&gt;data whose rows are changed by separate delete metadata.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The object listing answers, "What exists at this location?" The table metadata&lt;br&gt;
answers, "What belongs to this version or snapshot?" An investigation often&lt;br&gt;
needs both answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work from the selected state outward
&lt;/h2&gt;

&lt;p&gt;Suppose an Iceberg snapshot references 118 data files and 2 delete files. The&lt;br&gt;
storage prefix contains 126 Parquet objects.&lt;/p&gt;

&lt;p&gt;Scanning all 126 objects does not prove that the snapshot is wrong. Some&lt;br&gt;
objects may belong to an older snapshot or to no committed snapshot. Ignoring&lt;br&gt;
the 2 delete files can also count rows that the table considers deleted.&lt;/p&gt;

&lt;p&gt;Start with the reference the user selected:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Record the catalog, branch, tag, version, timestamp, or other table
reference used by the query.&lt;/li&gt;
&lt;li&gt;Resolve it to an exact Delta version or Iceberg snapshot ID.&lt;/li&gt;
&lt;li&gt;Follow the metadata chain and construct the active data-file and delete-file
sets.&lt;/li&gt;
&lt;li&gt;Check whether each referenced object is accessible. If the metadata records
a length or another identity that can be checked safely, compare that too.&lt;/li&gt;
&lt;li&gt;Apply the format's delete semantics before comparing row counts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do not begin with the newest-looking metadata filename. In Iceberg, the catalog&lt;br&gt;
or table reference chooses the current metadata. In Delta, the selected log&lt;br&gt;
version and its preceding state determine the snapshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Missing and extra files mean different things
&lt;/h2&gt;

&lt;p&gt;A referenced file that the reader cannot access makes the check incomplete.&lt;br&gt;
That observation alone does not distinguish a missing object from a temporary&lt;br&gt;
service or authorization failure.&lt;/p&gt;

&lt;p&gt;In Apache Iceberg issue 4168, a user reported a data-file upload failure&lt;br&gt;
followed by a manifest that still referenced the object. A later query failed&lt;br&gt;
when it needed that file. This is an example of the failure shape, not proof&lt;br&gt;
that every inaccessible reference has the same cause.&lt;/p&gt;

&lt;p&gt;The safe result is &lt;code&gt;incomplete&lt;/code&gt;. Treating an unreadable reference as zero rows&lt;br&gt;
would turn missing evidence into a clean-looking count.&lt;/p&gt;

&lt;p&gt;An unreferenced object needs a different interpretation. It might be retained&lt;br&gt;
for an older snapshot, left by an unsuccessful write, or waiting for approved&lt;br&gt;
maintenance. Its presence alone does not prove corruption, and deleting it&lt;br&gt;
during an investigation could damage a state that another reader still needs.&lt;/p&gt;

&lt;p&gt;Keep the first check read-only. Determine whether any committed state still&lt;br&gt;
references the object before considering cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  A successful query is one piece of evidence
&lt;/h2&gt;

&lt;p&gt;A successful query tells you that an engine returned rows. Record the exact&lt;br&gt;
table state separately before deciding what the result proves. The result does&lt;br&gt;
not, by itself, establish that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the query selected the state the user intended;&lt;/li&gt;
&lt;li&gt;every metadata reference and object was present;&lt;/li&gt;
&lt;li&gt;the reader understood every required table feature and delete type;&lt;/li&gt;
&lt;li&gt;another engine resolved the same catalog reference and cached metadata.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When two results disagree, save the selected version or snapshot, active-file&lt;br&gt;
counts, delete-file counts, and unsupported features before rerunning either&lt;br&gt;
query. A rerun against a newer state can erase the comparison you needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small evidence record
&lt;/h2&gt;

&lt;p&gt;The record does not need every metadata field. It needs enough information for&lt;br&gt;
another engineer to repeat the decision.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Question&lt;/td&gt;
&lt;td&gt;Why do the catalog query and folder scan differ by 211,372 rows?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selected state&lt;/td&gt;
&lt;td&gt;Iceberg snapshot 711.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata check&lt;/td&gt;
&lt;td&gt;Snapshot and manifest chain resolved.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File check&lt;/td&gt;
&lt;td&gt;118 referenced data files present; 8 additional objects not attributed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete check&lt;/td&gt;
&lt;td&gt;2 delete files apply to the selected snapshot.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;Incomplete until the folder scan applies the same active-file and delete rules.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Do not include credentials, signed URLs, private paths, SQL, schemas, customer&lt;br&gt;
information, or raw provider errors in the record.&lt;/p&gt;

&lt;p&gt;The two inputs are now specific: snapshot 711, interpreted through its data and&lt;br&gt;
delete manifests, and a scan of 126 physical objects. Make the folder scan use&lt;br&gt;
the same active-file and delete rules, then recount. That check can explain the&lt;br&gt;
211,372-row difference instead of running two vague queries again.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/delta-io/delta/blob/master/PROTOCOL.md" rel="noopener noreferrer"&gt;Delta Transaction Log Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://iceberg.apache.org/spec/#snapshots" rel="noopener noreferrer"&gt;Apache Iceberg specification: snapshots and manifests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/apache/iceberg/issues/4168" rel="noopener noreferrer"&gt;Apache Iceberg issue 4168: a referenced data file was unavailable&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One question for Delta and Iceberg users
&lt;/h2&gt;

&lt;p&gt;When a table's state looks wrong, which step usually costs you the most time?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identifying the exact version or snapshot&lt;/li&gt;
&lt;li&gt;Following the metadata chain&lt;/li&gt;
&lt;li&gt;Matching referenced files to object storage&lt;/li&gt;
&lt;li&gt;Understanding delete files or deletion vectors&lt;/li&gt;
&lt;li&gt;Reproducing the same result in another tool&lt;/li&gt;
&lt;li&gt;Something else&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A one-number comment is enough. Please do not post confidential data, file&lt;br&gt;
paths, SQL, schemas, credentials, customer information, or screenshots.&lt;/p&gt;

&lt;p&gt;If you prefer email, use the same one-question prompt on the&lt;br&gt;
&lt;a href="https://bigdatasight.com/research" rel="noopener noreferrer"&gt;BigdataSight research page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: We build BigdataSight, a native Mac data workstation. We are&lt;br&gt;
researching evidence-backed, local-first tools for data investigation,&lt;br&gt;
reproducible verification, and workflows that are read-only by default. The&lt;br&gt;
workflow described here is research, not a shipping capability or delivery&lt;br&gt;
commitment.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Writing note: I developed and verified the technical argument in this article. AI tools assisted with English drafting and editing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dataengineering</category>
      <category>iceberg</category>
      <category>lakehouse</category>
    </item>
    <item>
      <title>The pipeline passed. The data still needs proof.</title>
      <dc:creator>Rui Wang</dc:creator>
      <pubDate>Wed, 29 Jul 2026 13:22:55 +0000</pubDate>
      <link>https://dev.to/bigdatasight/the-pipeline-passed-the-data-still-needs-proof-4coa</link>
      <guid>https://dev.to/bigdatasight/the-pipeline-passed-the-data-still-needs-proof-4coa</guid>
      <description>&lt;p&gt;A pipeline job finishes without an error. The new table has 18% fewer rows.&lt;br&gt;
Should you rerun the pipeline, stop the release, or trust the new output?&lt;/p&gt;

&lt;p&gt;Before rerunning the job, preserve the two states you need to compare. A rerun&lt;br&gt;
can create another state and make the original change harder to explain. The&lt;br&gt;
first useful question is: which two states are we actually comparing?&lt;/p&gt;

&lt;h2&gt;
  
  
  Pin down the before and after
&lt;/h2&gt;

&lt;p&gt;"Yesterday" and "latest" sound precise until a file is replaced or a table&lt;br&gt;
advances to another snapshot.&lt;/p&gt;

&lt;p&gt;Give both states durable identities. That might mean a file checksum, an object&lt;br&gt;
version, a Delta table version, an Iceberg snapshot, a catalog reference, or a&lt;br&gt;
pipeline run ID. Record the local time and timezone too. They help when logs and&lt;br&gt;
object histories use different clocks.&lt;/p&gt;

&lt;p&gt;Suppose a Delta table's row-count drop occurred between versions 842 and 849.&lt;br&gt;
Those two numbers turn a vague report into a reproducible question. Without&lt;br&gt;
them, another engineer may inspect version 850 and reach a different conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the smallest useful explanation
&lt;/h2&gt;

&lt;p&gt;An unexpected nullable column and a lower row count point to different first&lt;br&gt;
checks.&lt;/p&gt;

&lt;p&gt;If a column became nullable, compare the two schemas before scanning rows.&lt;br&gt;
Check whether the field changed type, moved inside a nested structure, or was&lt;br&gt;
renamed. A CSV inference change and a Parquet schema change may look similar in&lt;br&gt;
a grid, but they are different events.&lt;/p&gt;

&lt;p&gt;If the row count changed, group counts by the partition or date most likely to&lt;br&gt;
explain it. A full profile of every column may cost more than the original query&lt;br&gt;
while hiding the useful difference.&lt;/p&gt;

&lt;p&gt;Delta and Iceberg add another question: which logical table state did the&lt;br&gt;
reader resolve? The newest objects in storage do not necessarily belong to the&lt;br&gt;
selected state. Check the version or snapshot, the metadata chain, and the data&lt;br&gt;
or delete files involved before blaming an individual file.&lt;/p&gt;

&lt;p&gt;The aim is a smaller search area. A larger report can wait until the evidence&lt;br&gt;
calls for one.&lt;/p&gt;

&lt;h2&gt;
  
  
  State what the check can prove
&lt;/h2&gt;

&lt;p&gt;The same finding means something different when it comes from metadata, a&lt;br&gt;
sample, or every relevant row.&lt;/p&gt;

&lt;p&gt;Metadata can establish recorded schema, partition information, and available&lt;br&gt;
statistics. It cannot prove a claim about every row. A sample helps discover a&lt;br&gt;
pattern, but a missing value in the sample does not prove that the value is&lt;br&gt;
absent. A full scan supports stronger row-level claims, provided it covered&lt;br&gt;
every relevant partition and delete.&lt;/p&gt;

&lt;p&gt;Record truncation, skipped objects, unsupported semantics, cancellation, and&lt;br&gt;
fallback behavior. If one of them prevents a conclusion, call the result&lt;br&gt;
&lt;code&gt;incomplete&lt;/code&gt;. That is more useful than a clean-looking result with a hidden gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leave evidence another engineer can challenge
&lt;/h2&gt;

&lt;p&gt;A short record is enough when another engineer can understand the decision and&lt;br&gt;
repeat the check.&lt;/p&gt;

&lt;p&gt;A fictional record might look like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Recorded value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Question&lt;/td&gt;
&lt;td&gt;Why did the table lose 18% of its rows?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inputs&lt;/td&gt;
&lt;td&gt;Delta table versions 842 and 849.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checks&lt;/td&gt;
&lt;td&gt;Schema comparison and row counts grouped by day.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Metadata plus a full scan of the affected partition.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outcome&lt;/td&gt;
&lt;td&gt;Warning. The drop is isolated to July 25, 2026.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Limit&lt;/td&gt;
&lt;td&gt;Delete semantics were not checked, so the cause remains unproven.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Keep credentials, signed URLs, private paths, secret-bearing SQL, customer data,&lt;br&gt;
and private screenshots out of the record.&lt;/p&gt;

&lt;p&gt;Before running the checks, define the finish line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pass&lt;/strong&gt; means the stated rule held over the recorded scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warning&lt;/strong&gt; means the result needs review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure&lt;/strong&gt; means the evidence disproved the rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incomplete&lt;/strong&gt; means the available evidence cannot support a conclusion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Leave enough evidence for another engineer to reproduce the result, disagree&lt;br&gt;
with it, and run the investigation again.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question for data engineers
&lt;/h2&gt;

&lt;p&gt;Which part of this work usually costs you the most time?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understanding an unexpected schema change&lt;/li&gt;
&lt;li&gt;Finding why row counts or profiles changed&lt;/li&gt;
&lt;li&gt;Understanding the real state of a Delta or Iceberg table&lt;/li&gt;
&lt;li&gt;Comparing pipeline output before and after a change&lt;/li&gt;
&lt;li&gt;Proving that a delivery or release is safe&lt;/li&gt;
&lt;li&gt;Something else&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A one-number comment is enough. Please do not post confidential data, file&lt;br&gt;
paths, SQL, schemas, credentials, customer information, or screenshots.&lt;/p&gt;

&lt;p&gt;If you prefer email, the same one-question prompt is available on the&lt;br&gt;
&lt;a href="https://bigdatasight.com/research" rel="noopener noreferrer"&gt;BigdataSight research page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I build BigdataSight, a native Mac data workstation. We are&lt;br&gt;
researching evidence-backed tools for investigating changes in files and&lt;br&gt;
lakehouse tables. The product direction described here remains research and is&lt;br&gt;
not a shipping capability.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Writing note: I developed and verified the technical argument in this article. AI tools assisted with English drafting and editing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dataengineering</category>
      <category>database</category>
      <category>bigdata</category>
      <category>lakehouse</category>
    </item>
  </channel>
</rss>
