<?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: Michael Neang</title>
    <description>The latest articles on DEV Community by Michael Neang (@mneang).</description>
    <link>https://dev.to/mneang</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%2F3288241%2F09979270-03c1-4149-9178-5276e4b875b8.jpeg</url>
      <title>DEV Community: Michael Neang</title>
      <link>https://dev.to/mneang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mneang"/>
    <language>en</language>
    <item>
      <title>My AI Agent Didn’t Break. Its Tool Did.</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Mon, 31 Aug 2026 02:57:26 +0000</pubDate>
      <link>https://dev.to/mneang/my-ai-agent-didnt-break-its-tool-did-58ip</link>
      <guid>https://dev.to/mneang/my-ai-agent-didnt-break-its-tool-did-58ip</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I created this piece of content for the purposes of entering the &lt;a href="https://allthingsagentichackathon.devpost.com/" rel="noopener noreferrer"&gt;All Things Agentic Hackathon&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A lot of agent demos assume the world around the agent stays still.&lt;/p&gt;

&lt;p&gt;The prompt is the same. The tools are the same. The API behaves the same way every time.&lt;/p&gt;

&lt;p&gt;Real systems do not get that luxury.&lt;/p&gt;

&lt;p&gt;A provider can rename a field, restructure a response, change an enum, switch units, or change the behavior behind an operation. The agent itself may be completely fine, but suddenly it can no longer complete the job it completed yesterday.&lt;/p&gt;

&lt;p&gt;That was the idea I kept coming back to while building &lt;strong&gt;ToolSuture&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What if I repaired compatibility around the deployed agent instead of rewriting the agent itself?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That became the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrmdwtbp2anzig6nd3c6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrmdwtbp2anzig6nd3c6.png" alt="ToolSuture — APIs change. Your agent shouldn't have to." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure I wanted to reproduce
&lt;/h2&gt;

&lt;p&gt;Most discussions about agent reliability start inside the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did it hallucinate?&lt;/li&gt;
&lt;li&gt;Did it call the wrong tool?&lt;/li&gt;
&lt;li&gt;Did it plan badly?&lt;/li&gt;
&lt;li&gt;Should it retry?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are important problems, but I wanted to look at a different one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when the agent is still correct and the tool changes underneath it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the main ToolSuture scenario, I froze a Google ADK shipment agent that expects a v1 provider contract.&lt;/p&gt;

&lt;p&gt;Then I changed the provider.&lt;/p&gt;

&lt;p&gt;The v2 tool still represents the same underlying shipment capability, but the contract is different enough that the original agent no longer understands it correctly.&lt;/p&gt;

&lt;p&gt;The usual answer would be to update the integration and redeploy the agent.&lt;/p&gt;

&lt;p&gt;I wanted ToolSuture to try something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recover the capability, not the agent
&lt;/h2&gt;

&lt;p&gt;ToolSuture runs a recovery loop:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observe → Diagnose → Policy → Plan → Validate → Repair → Replay → Verify&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It starts with the original mission, the old tool contract, the new tool contract, provider semantics, and runtime evidence.&lt;/p&gt;

&lt;p&gt;Then &lt;strong&gt;Gemini 3.6 Flash through Vertex AI&lt;/strong&gt; handles the part I did not want to reduce to a pile of string comparisons:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do these two contracts still mean the same thing for this mission?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That matters because schema similarity and semantic equivalence are not the same thing.&lt;/p&gt;

&lt;p&gt;If the migration is still semantically equivalent, ToolSuture creates a bounded compatibility repair around the frozen agent.&lt;/p&gt;

&lt;p&gt;It does not rewrite the agent.&lt;/p&gt;

&lt;p&gt;It then replays the original mission against the changed provider and checks whether the recovered workflow actually worked.&lt;/p&gt;

&lt;p&gt;The primary demo ends with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MISSION COMPLETED AND VERIFIED
CAPABILITY_LOST → CAPABILITY_RESTORED
0 BYTES CHANGED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0du0bpdx9ojbus8mlf0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0du0bpdx9ojbus8mlf0t.png" alt="The original deployed agent completes the mission again against Provider V2 without being rewritten." width="799" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The part I care about most there is not the green UI.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;0 BYTES CHANGED&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The same deployed agent is still there. ToolSuture repaired the compatibility around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The harder case was knowing when to stop
&lt;/h2&gt;

&lt;p&gt;Once I had the safe recovery working, I did not want the demo to imply that every API change should be repaired automatically.&lt;/p&gt;

&lt;p&gt;That would be reckless.&lt;/p&gt;

&lt;p&gt;So I built a second scenario where the change looks superficially manageable but the meaning is different.&lt;/p&gt;

&lt;p&gt;The original tool moves a draft into recoverable trash with a 30-day recovery window.&lt;/p&gt;

&lt;p&gt;The new provider changes that behavior into irreversible permanent deletion.&lt;/p&gt;

&lt;p&gt;At a schema level, those operations can still look related.&lt;/p&gt;

&lt;p&gt;Operationally, they are not the same action.&lt;/p&gt;

&lt;p&gt;ToolSuture refuses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REFUSE
CRITICAL
BLOCKED
SAFE_HOLD
0 EXECUTION ATTEMPTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femxpx8jrs8g5hiehuxka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femxpx8jrs8g5hiehuxka.png" alt="The red path is intentional. ToolSuture stops before external execution when the semantics become more destructive." width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I actually like this result as much as the successful recovery.&lt;/p&gt;

&lt;p&gt;The safe case says:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;this changed, but it still means the same thing — act.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dangerous case says:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;this looks related, but it no longer means the same thing — stop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is a much more useful definition of autonomy than simply giving an agent permission to do more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture decision that made the project click
&lt;/h2&gt;

&lt;p&gt;The biggest improvement came when I stopped treating recovery as one big AI step.&lt;/p&gt;

&lt;p&gt;ToolSuture separates three jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Semantic reasoning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Gemini 3.6 Flash on Vertex AI&lt;/strong&gt; interprets whether the old and new tool contracts preserve the meaning required by the original mission.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution authority
&lt;/h3&gt;

&lt;p&gt;Gemini does not get unrestricted authority to execute arbitrary generated fixes.&lt;/p&gt;

&lt;p&gt;A deterministic policy and validation layer decides whether the proposed migration stays inside the allowed repair envelope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification
&lt;/h3&gt;

&lt;p&gt;The recovery path is not allowed to declare itself successful just because the replay looked convincing.&lt;/p&gt;

&lt;p&gt;A separate verifier checks fresh provider evidence tied to that specific replay.&lt;/p&gt;

&lt;p&gt;That led to the design principle I kept using throughout the project:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The component that performs recovery does not get to certify that recovery succeeded.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8drybvb6xpfx1yt7xa27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8drybvb6xpfx1yt7xa27.png" alt="ToolSuture separates semantic reasoning, execution authority, and independent verification." width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rest of the stack is intentionally straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Agent Development Kit (ADK)&lt;/strong&gt; for the frozen deployed agent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google GenAI SDK&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud Run&lt;/strong&gt; for the public application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FastAPI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built ToolSuture for the &lt;a href="https://allthingsagentichackathon.devpost.com/" rel="noopener noreferrer"&gt;All Things Agentic Hackathon&lt;/a&gt;, so I also wanted the Google stack to be doing real work rather than appearing in the architecture diagram as decoration.&lt;/p&gt;

&lt;p&gt;Gemini is responsible for semantic reasoning.&lt;/p&gt;

&lt;p&gt;ADK is the framework used by the frozen agent.&lt;/p&gt;

&lt;p&gt;Cloud Run hosts the live application.&lt;/p&gt;

&lt;h2&gt;
  
  
  I wanted evidence, not just a nice result screen
&lt;/h2&gt;

&lt;p&gt;One thing I have become increasingly skeptical of in agent demos is the phrase:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The agent says it succeeded.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is not the same as proving the external task succeeded.&lt;/p&gt;

&lt;p&gt;So ToolSuture records and verifies evidence from the current replay instead of trusting the agent's own success message.&lt;/p&gt;

&lt;p&gt;The current evaluation includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 / 3&lt;/strong&gt; repeated verified Cloud recoveries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;16 / 16&lt;/strong&gt; independent checks on the primary safe replay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5 / 5&lt;/strong&gt; blind evaluation scenarios using the frozen recovery engine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0 bytes changed&lt;/strong&gt; to the deployed agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0 execution attempts&lt;/strong&gt; in the dangerous migration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those numbers are less flashy than adding another feature, but they made me trust the system more.&lt;/p&gt;

&lt;p&gt;The goal was never:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Generate a plausible migration plan.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Restore the original capability and prove that it works again.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;The biggest lesson for me was that AI reasoning is more useful when its authority has clear boundaries.&lt;/p&gt;

&lt;p&gt;Gemini is good at interpreting semantic relationships that would be painful to encode as static rules.&lt;/p&gt;

&lt;p&gt;But interpreting something, authorizing it, executing it, and verifying it are four different responsibilities.&lt;/p&gt;

&lt;p&gt;Separating those responsibilities made ToolSuture much easier to reason about.&lt;/p&gt;

&lt;p&gt;I also came away thinking differently about agent reliability.&lt;/p&gt;

&lt;p&gt;Before this project, I mostly thought about whether the agent itself could complete a task.&lt;/p&gt;

&lt;p&gt;Now I think the better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can the capability survive changes outside the agent, and can the system prove that it still works afterward?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is what ToolSuture is testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try ToolSuture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://toolsuture-fx7bbo3mpq-wl.a.run.app/" rel="noopener noreferrer"&gt;https://toolsuture-fx7bbo3mpq-wl.a.run.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/mneang/toolsuture" rel="noopener noreferrer"&gt;https://github.com/mneang/toolsuture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hackathon:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://allthingsagentichackathon.devpost.com/" rel="noopener noreferrer"&gt;https://allthingsagentichackathon.devpost.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;APIs change. Your agent shouldn't have to.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>api</category>
      <category>allthingsagentichackathon</category>
    </item>
    <item>
      <title>False Green: My Incident Agent Said “Healthy” Without Live Evidence</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Mon, 24 Aug 2026 04:08:29 +0000</pubDate>
      <link>https://dev.to/mneang/false-green-my-incident-agent-said-healthy-without-live-evidence-2di3</link>
      <guid>https://dev.to/mneang/false-green-my-incident-agent-said-healthy-without-live-evidence-2di3</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Clear the Lineup&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The pipeline was healthy.&lt;/p&gt;

&lt;p&gt;At least, that was what my incident agent told me.&lt;/p&gt;

&lt;p&gt;There was one problem: it could not see the live Fivetran pipeline.&lt;/p&gt;

&lt;p&gt;The live check had failed, so the app fell back to cached evidence. That snapshot still said &lt;code&gt;connected&lt;/code&gt; and &lt;code&gt;on_schedule&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The final verdict was still:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Healthy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nothing crashed.&lt;/p&gt;

&lt;p&gt;The request succeeded.&lt;/p&gt;

&lt;p&gt;The fallback data looked healthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The conclusion was still wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The application had taken the last known state and quietly treated it like the current one.&lt;/p&gt;

&lt;p&gt;That was the false green.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Current confidence requires current evidence.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;Pipeline Rescue Agent is a Next.js incident-investigation app that combines connector-health and data-freshness signals to decide what still needs investigation.&lt;/p&gt;

&lt;p&gt;Gemini runs later in the recovery-planning flow. The false-green verdict occurred in deterministic application logic before the model was involved.&lt;/p&gt;

&lt;p&gt;The question at this decision point was simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this pipeline healthy right now?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I found a case where the application was more confident than its evidence allowed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bug Fix or Performance Improvement
&lt;/h2&gt;

&lt;p&gt;When live Fivetran telemetry became unavailable, Pipeline Rescue Agent fell back to cached connector evidence.&lt;/p&gt;

&lt;p&gt;I wanted to keep that fallback. The last known connector state can still be useful during an incident.&lt;/p&gt;

&lt;p&gt;The failed live path was the trigger condition.&lt;/p&gt;

&lt;p&gt;It was not the application defect.&lt;/p&gt;

&lt;p&gt;The defect was what happened afterward: cached evidence was still allowed to support a current &lt;code&gt;Healthy&lt;/code&gt; verdict.&lt;/p&gt;

&lt;p&gt;Before the fix, the decision state looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evidence.mode = cached_fivetran_evidence
evidence.live = false

fivetran.mcp_ok = false
fivetran.setup_state = connected
fivetran.update_state = on_schedule

pipeline.health_verdict = healthy
false_green.detected = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Debugging chronology
&lt;/h3&gt;

&lt;p&gt;There are three intentional repository states in this investigation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;6194a3b14efd33a0d5f8235f703187005123a0f9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Historical tagged baseline, before Sentry instrumentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;b3a45110&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sentry instrumentation added while the decision bug was still intact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;b05d2cccf93713f6a52a4a4ec6fa6b1600cbec86&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Final fixed state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The sequence was deliberate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reproduce
→ instrument the still-broken decision
→ inspect
→ diagnose
→ fix
→ verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2vbss0urb6n7otbiqw3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2vbss0urb6n7otbiqw3p.png" alt="Pre-fix Sentry decision span showing cached Fivetran evidence, live evidence false, MCP unavailable, connected and on-schedule status, and a Healthy pipeline verdict" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pre-fix Sentry decision span: cached, non-live evidence still produced a Healthy verdict.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Almost every individual status value looked reasonable.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/api/investigate&lt;/code&gt; request itself completed successfully. The fallback returned data. The cached connector status said &lt;code&gt;connected&lt;/code&gt; and &lt;code&gt;on_schedule&lt;/code&gt;. There were no active warnings or tasks.&lt;/p&gt;

&lt;p&gt;The endpoint completed successfully; the semantic failure itself raised no exception.&lt;/p&gt;

&lt;p&gt;The contradiction only appeared when evidence provenance and the final decision were inspected together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cached evidence
+ live = false
+ MCP unavailable
→ Healthy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache itself was doing its job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The health gate was asking the cache to prove something it could not prove.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cached evidence could answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What was last known?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It could not answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is the pipeline healthy right now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why this mattered
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Healthy&lt;/code&gt; was not only a displayed status.&lt;/p&gt;

&lt;p&gt;The connector decision feeds the agent's final &lt;code&gt;pipelineStatus&lt;/code&gt;, which &lt;code&gt;getLikelyIssue&lt;/code&gt; uses when choosing what the investigation should examine next.&lt;/p&gt;

&lt;p&gt;When connector health cannot be established:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Needs review
→ likely issue: Connector health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the connector is considered healthy while destination data is stale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Healthy + stale data
→ likely issue: Upstream source freshness
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A false green did not merely change the displayed status; it could change what the investigation examined next.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I did not need the application to assume Fivetran was broken. I needed it to stop clearing connector health when current connector evidence was unavailable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sentry Exposed the Contradiction
&lt;/h2&gt;

&lt;p&gt;I first reproduced the suspicious &lt;code&gt;Healthy&lt;/code&gt; result through &lt;code&gt;/api/investigate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before fixing it, I added a custom Sentry &lt;code&gt;pipeline.decision&lt;/code&gt; span containing the runtime state behind that decision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evidence.mode
evidence.live
fivetran.mcp_ok
fivetran.setup_state
fivetran.update_state
pipeline.health_verdict
false_green.detected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a silent correctness bug.&lt;/p&gt;

&lt;p&gt;The request worked. The fallback worked. The values looked plausible. The failure was in the conclusion drawn from them.&lt;/p&gt;

&lt;p&gt;Sentry put the healthy-looking status content, its non-live provenance, and the final verdict in the same trace.&lt;/p&gt;

&lt;p&gt;That changed the debugging question from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why does this endpoint say &lt;code&gt;Healthy&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why is non-live evidence allowed to support &lt;code&gt;Healthy&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Seer and the root cause
&lt;/h3&gt;

&lt;p&gt;I used Seer to analyze the captured failing trace.&lt;/p&gt;

&lt;p&gt;It highlighted evidence currentness as the likely missing decision constraint: healthy-looking cached values were reaching a verdict path that did not enforce live provenance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3msebqldfr3jm0qzd7en.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3msebqldfr3jm0qzd7en.png" alt="Seer root-cause analysis identifying that healthy-looking cached Fivetran status was evaluated without enforcing evidence currentness or provenance" width="799" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Seer RCA: the failing trace pointed to evidence currentness/provenance as the missing decision constraint.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I treated that as a hypothesis and verified it against the source.&lt;/p&gt;

&lt;p&gt;I also did not copy the generated suggestion literally.&lt;/p&gt;

&lt;p&gt;Pipeline Rescue Agent supports two legitimate live evidence paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mcp_live
live
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A direct live Fivetran API response can still be current evidence even when the MCP path is unavailable.&lt;/p&gt;

&lt;p&gt;So the correct invariant was not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Healthy requires MCP success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Healthy requires live evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Missing Invariant
&lt;/h2&gt;

&lt;p&gt;The bug reduced to one safety property:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A &lt;code&gt;Healthy&lt;/code&gt; verdict requires live evidence.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Healthy ⇒ evidence.live = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failing Sentry trace violated it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evidence.live = false
pipeline.health_verdict = healthy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache could remain; only the decision boundary had to change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;PR: &lt;strong&gt;&lt;a href="https://github.com/mneang/pipeline-rescue-agent/pull/1" rel="noopener noreferrer"&gt;Fix false-green pipeline health verdicts from cached Fivetran evidence&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I isolated the health decision in a small helper used by the production route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;evaluateFivetranHealth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setupState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;updateState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;hasWarnings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;hasTasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasLiveEvidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mcp_live&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;live&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isHealthy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;hasLiveEvidence&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;setupState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;updateState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;on_schedule&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasWarnings&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasTasks&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;paused&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;hasLiveEvidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;isHealthy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important distinction is small:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evidence&lt;/th&gt;
&lt;th&gt;What it may establish&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Live&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;May certify current health&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cached&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Historical context only; cannot independently certify current health&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Historical evidence can still inform the investigation.&lt;/p&gt;

&lt;p&gt;Only current evidence may certify current health.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Improvements
&lt;/h2&gt;

&lt;p&gt;I checked the surrounding investigation flow for the same provenance assumption. Cached Fivetran evidence no longer appears as a successful current connector check, no longer counts as live evidence, and is no longer described in recovery reasoning as if it represented current health. The production rule now lives in the small pure helper above, which also makes the decision boundary directly testable.&lt;/p&gt;




&lt;h2&gt;
  
  
  After: Same Non-Live Evidence Class, Safer Decision
&lt;/h2&gt;

&lt;p&gt;The final Sentry trace from commit &lt;code&gt;b05d2cccf93713f6a52a4a4ec6fa6b1600cbec86&lt;/code&gt; shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evidence.mode = cached_fivetran_evidence
evidence.live = false

fivetran.mcp_ok = false
fivetran.setup_state = connected
fivetran.update_state = on_schedule

provenance.guard_applied = true
pipeline.health_verdict = needs_review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9bb1o6lmq5kjwv8ety7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9bb1o6lmq5kjwv8ety7z.png" alt="Final Sentry decision span showing cached non-live Fivetran evidence with the provenance guard applied and the pipeline verdict changed to Needs review" width="799" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Final Sentry decision span: the same non-live evidence class now produces Needs review.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The status values still look superficially healthy.&lt;/p&gt;

&lt;p&gt;The live telemetry is still unavailable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix did not make unavailable telemetry available. It changed what conclusion the application was allowed to derive from that uncertainty.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cached + non-live
→ Needs review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Historical RED → Final GREEN
&lt;/h2&gt;

&lt;p&gt;I wanted stronger verification than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The patched application passes a test written after patching it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I moved the decisive assertion outside the repository and ran it unchanged against the tagged historical application and the final branch.&lt;/p&gt;

&lt;p&gt;The script imports no application decision logic. It intentionally reads only response fields available in both versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timeline[].evidence.mode
agentRun.decision.pipelineStatus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also refuses to count a run if the expected cached-fallback condition was not reproduced.&lt;/p&gt;

&lt;p&gt;In simplified form, the external assertion enforced three conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cached_fivetran_evidence&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TEST INVALID: cached fallback was not reproduced&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Healthy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Needs review&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unexpected verdict: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran that same external assertion against the historical baseline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Commit:
6194a3b14efd33a0d5f8235f703187005123a0f9

Evidence mode:
cached_fivetran_evidence

Pipeline verdict:
Healthy

Result:
FAIL

Exit code:
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I ran it against the final branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Commit:
b05d2cccf93713f6a52a4a4ec6fa6b1600cbec86

Evidence mode:
cached_fivetran_evidence

Pipeline verdict:
Needs review

Result:
PASS

Exit code:
0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzw84ecy9u3b5f9lelhci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzw84ecy9u3b5f9lelhci.png" alt="Terminal comparison showing the historical baseline returning Healthy and failing the external black-box check, while the final branch returns Needs review and passes" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;External black-box check: historical baseline fails; final branch passes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This comparison does not claim that every low-level cause of telemetry unavailability was identical between the two executions.&lt;/p&gt;

&lt;p&gt;That is not what the assertion tests.&lt;/p&gt;

&lt;p&gt;The controlled application condition is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fivetran evidence is cached rather than live.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can that evidence still certify current health?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Historical application:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Final application:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Decision-Boundary Positive Control
&lt;/h2&gt;

&lt;p&gt;There was one more bad fix I wanted to rule out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if &lt;code&gt;Healthy&lt;/code&gt; simply became impossible?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same pure predicate imported by the production route was checked on both sides of the boundary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Expected&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;cached_fivetran_evidence&lt;/code&gt; + clean status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Healthy = false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;mcp_live&lt;/code&gt; + clean status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Healthy = true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;live&lt;/code&gt; + clean status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Healthy = true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is intentionally a decision-boundary check.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; claim to contact a live MCP server or the live Fivetran API.&lt;/p&gt;

&lt;p&gt;Its purpose is narrower: both supported live evidence modes remain eligible for &lt;code&gt;Healthy&lt;/code&gt;, while cached evidence does not.&lt;/p&gt;

&lt;p&gt;The non-live application behavior is separately verified through &lt;code&gt;/api/investigate&lt;/code&gt; and the external black-box assertion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;Final branch verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test:health-invariant
PASS

PIPELINE_RESCUE_URL=http://localhost:3001 npm run test:false-green
PASS

External black-box check
PASS
exit 0

npm run lint
PASS

npm run build
PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTTP regression check confirms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evidence mode: cached_fivetran_evidence
Live evidence: false
Pipeline verdict: Needs review

PASS: non-live cached evidence cannot produce a Healthy verdict.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Best Use of Sentry
&lt;/h2&gt;

&lt;p&gt;This was exactly where Sentry mattered: &lt;code&gt;/api/investigate&lt;/code&gt; completed successfully, so ordinary error monitoring did not expose the semantic failure.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;pipeline.decision&lt;/code&gt; span made provenance, liveness, connector state, and final verdict observable together inside that successful request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sentry did not merely verify the repair after the fact: I captured the contradiction from the still-buggy release, used that failing trace as runtime context for Seer, and then inspected the same decision again after the fix.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seer then helped narrow that runtime contradiction to evidence currentness, which I verified against the source before implementing the narrower application invariant.&lt;/p&gt;

&lt;p&gt;After the fix, the final trace shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evidence.live = false
provenance.guard_applied = true
pipeline.health_verdict = needs_review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sentry did not replace the debugging.&lt;/p&gt;

&lt;p&gt;It exposed the hidden decision state that ordinary success/failure monitoring would not have explained.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Result
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEFORE

No live Fivetran telemetry
→ cached healthy-looking evidence
→ Healthy


AFTER

No live Fivetran telemetry
→ cached evidence retained as context
→ live-evidence guard
→ Needs review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache can still tell an investigator what was last known.&lt;/p&gt;

&lt;p&gt;It just no longer gets to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is the pipeline healthy right now?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Current confidence requires current evidence.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;PR: &lt;strong&gt;&lt;a href="https://github.com/mneang/pipeline-rescue-agent/pull/1" rel="noopener noreferrer"&gt;github.com/mneang/pipeline-rescue-agent/pull/1&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>agents</category>
      <category>ai</category>
    </item>
    <item>
      <title>Tifo Forge: Turning Football Passion Into a Stadium Tifo</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Mon, 13 Jul 2026 00:35:28 +0000</pubDate>
      <link>https://dev.to/mneang/tifo-forge-turning-football-passion-into-a-stadium-tifo-379d</link>
      <guid>https://dev.to/mneang/tifo-forge-turning-football-passion-into-a-stadium-tifo-379d</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;During the &lt;strong&gt;World Cup&lt;/strong&gt;, millions of people can watch the same match.&lt;/p&gt;

&lt;p&gt;But every stadium tries to say something different before kickoff.&lt;/p&gt;

&lt;p&gt;Sometimes it is belief. Sometimes defiance. Sometimes memory. Sometimes unity.&lt;/p&gt;

&lt;p&gt;I follow football closely, and some of the moments I remember most are not goals. They are the few seconds before kickoff when the camera pulls wide and an entire stand reveals one message at once.&lt;/p&gt;

&lt;p&gt;That was the idea behind &lt;strong&gt;Tifo Forge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is an interactive experience that turns a team, a supporter emotion, and a symbol into an animated stadium tifo.&lt;/p&gt;

&lt;p&gt;Not another match tracker. Not another football chatbot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tifo Forge&lt;/strong&gt; turns supporter emotion into a stadium moment.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Tifo Forge asks the user to make three choices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A national team&lt;/li&gt;
&lt;li&gt;A supporter emotion&lt;/li&gt;
&lt;li&gt;A visual symbol&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The emotions are simple on purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Believe&lt;/li&gt;
&lt;li&gt;Defy&lt;/li&gt;
&lt;li&gt;Unite&lt;/li&gt;
&lt;li&gt;Remember&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The symbols include ideas such as lightning, a phoenix, wings, a heart, and dawn.&lt;/p&gt;

&lt;p&gt;Once those choices are made, Gemini creates a structured design plan. The browser then turns that plan into an animated stadium display.&lt;/p&gt;

&lt;p&gt;I deliberately avoided uploads, accounts, and long setup screens. I wanted someone to open the page and reach the reveal in under a minute.&lt;/p&gt;

&lt;p&gt;Three choices are enough to raise the stand.&lt;/p&gt;

&lt;p&gt;The final result can be replayed, reset, or saved as an SVG poster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Try Tifo Forge:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tifo-forge.vercel.app/" rel="noopener noreferrer"&gt;https://tifo-forge.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqwnxh78amlmozo2ha1d.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqwnxh78amlmozo2ha1d.gif" alt="Animated Tifo Forge reveal for Spain showing a dark stadium stand transform into a red and yellow lightning-themed tifo with the chant “SHOCK THE WORLD,” directed by Gemini." width="560" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I kept thinking about those few seconds before kickoff when everyone in the stadium knows something is about to happen, but nobody has seen the full picture yet.&lt;/p&gt;

&lt;p&gt;That became the interaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Choose the team
      ↓
Choose the feeling
      ↓
Choose the symbol
      ↓
Raise the tifo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user clicks &lt;strong&gt;Raise the Tifo&lt;/strong&gt;, the stadium darkens.&lt;/p&gt;

&lt;p&gt;Rows of cards flip into place. The pattern spreads across the curved stand. The central symbol appears, and the chant locks into position.&lt;/p&gt;

&lt;p&gt;The user is not asking for a random poster.&lt;/p&gt;

&lt;p&gt;They are deciding what the stand believes, how it moves, and what it raises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One feeling. One stand. One voice.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frvo2anr4287won2bjco8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frvo2anr4287won2bjco8.png" alt="Tifo Forge interface showing Japan, Unite, and Dawn selected, alongside a completed red and white stadium tifo, the chant “STAND AND STRIKE,” and a Gemini creative direction panel with formation, tempo, and intensity." width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Japan · Unite · Dawn — the selected inputs, Gemini direction, and completed stand in one view.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  One Real Example: Spain, Defy, Lightning
&lt;/h2&gt;

&lt;p&gt;The easiest way to explain the system is with one real generation.&lt;/p&gt;

&lt;p&gt;The user chooses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Team: Spain
Emotion: Defy
Symbol: Lightning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemini returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"slogan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SHOCK THE WORLD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"radiating-burst"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"animation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stadium-pulse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"energy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"designReason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A radiating burst of electric energy mimics a lightning strike, galvanizing the crowd to defy all doubts and roar as one."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each value changes something visible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;slogan&lt;/code&gt; becomes the chant&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;layout&lt;/code&gt; controls the mosaic geometry&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;animation&lt;/code&gt; controls how the reveal moves&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;energy&lt;/code&gt; controls the visual intensity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this result, &lt;code&gt;radiating-burst&lt;/code&gt; maps to a center-weighted SVG card formation, while &lt;code&gt;stadium-pulse&lt;/code&gt; staggers the timing across the stand.&lt;/p&gt;

&lt;p&gt;Gemini selects from a controlled set of supported layouts and animations, so every plan maps to a renderer path that already exists and has been tested.&lt;/p&gt;

&lt;p&gt;The model chooses the direction.&lt;/p&gt;

&lt;p&gt;The renderer handles the geometry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxf12lohbr4uswj4wl07v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxf12lohbr4uswj4wl07v.png" alt="Completed Tifo Forge result for Spain with a red and yellow radiating stadium pattern, lightning symbol, the chant “SHOCK THE WORLD,” and a Gemini creative direction panel showing a radiating-burst formation, stadium-pulse motion, and intensity 95." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spain · Defy · Lightning — Gemini selected the message, formation, motion, and intensity; the procedural renderer executed the final stand.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The full source is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mneang/tifo-forge" rel="noopener noreferrer"&gt;https://github.com/mneang/tifo-forge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository includes the Gemini API route, structured-output validation, procedural SVG renderer, animation system, accessibility behavior, replay and export controls, and the quota-resilient fallback flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How Gemini Works Inside Tifo Forge
&lt;/h3&gt;

&lt;p&gt;I did not want Gemini writing HTML, CSS, or SVG.&lt;/p&gt;

&lt;p&gt;That is too unpredictable for an experience where the reveal has to stay clean.&lt;/p&gt;

&lt;p&gt;Instead, I wanted Gemini making creative decisions while the application handled everything deterministic.&lt;/p&gt;

&lt;p&gt;Gemini connects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Team identity
+
Supporter emotion
+
Visual symbol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and turns them into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
+
Formation
+
Motion
+
Intensity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response comes back as constrained JSON.&lt;/p&gt;

&lt;p&gt;That output is validated with Zod, normalized, and then passed into the procedural renderer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Supporter selections
        ↓
Gemini creative direction
        ↓
Structured-output validation
        ↓
Procedural SVG renderer
        ↓
Animated stadium reveal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This split kept the project manageable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gemini interprets the inputs&lt;/li&gt;
&lt;li&gt;validation keeps the response usable&lt;/li&gt;
&lt;li&gt;the renderer controls the final experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without Gemini, the project would become a fixed rules engine with much less variation.&lt;/p&gt;

&lt;p&gt;Without the renderer, the visual output would be much harder to control.&lt;/p&gt;

&lt;p&gt;I kept Gemini's job narrow: turn three choices into a design plan the application could actually use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb3r5hml2p4ap1sq1mklq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb3r5hml2p4ap1sq1mklq.png" alt="Architecture diagram showing supporter selections flowing into Gemini creative direction, structured-output validation, a procedural SVG renderer, and the final animated stadium tifo." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Tifo Direction Framework
&lt;/h3&gt;

&lt;p&gt;I used four dimensions to keep each result focused.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Message
&lt;/h4&gt;

&lt;p&gt;Can thousands of supporters read and repeat it quickly?&lt;/p&gt;

&lt;p&gt;The chant has to be short, emotional, and suitable for a large banner.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Formation
&lt;/h4&gt;

&lt;p&gt;Does the layout communicate the feeling before the explanation is read?&lt;/p&gt;

&lt;p&gt;A radiating burst feels different from a split banner or a diagonal rise.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Tempo
&lt;/h4&gt;

&lt;p&gt;How should the stand reveal itself?&lt;/p&gt;

&lt;p&gt;A united design may build row by row. A defiant design may strike outward or sweep diagonally.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Intensity
&lt;/h4&gt;

&lt;p&gt;How much visual energy fits the chosen feeling?&lt;/p&gt;

&lt;p&gt;Remember should not move like Defy. Believe should not always feel like Unite.&lt;/p&gt;

&lt;p&gt;Having these four dimensions also kept me honest while building. Every layout and animation had to answer one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this actually communicate the chosen emotion?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Technical Execution
&lt;/h3&gt;

&lt;p&gt;Tifo Forge was built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js and TypeScript&lt;/li&gt;
&lt;li&gt;Google Gemini API&lt;/li&gt;
&lt;li&gt;Zod structured-output validation&lt;/li&gt;
&lt;li&gt;Motion for React&lt;/li&gt;
&lt;li&gt;Procedural SVG rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added visible keyboard focus, reduced-motion behavior, replay, reset, and SVG download so the experience works beyond one ideal desktop demo.&lt;/p&gt;

&lt;p&gt;Each team uses a curated palette inspired by its current football identity, while avoiding historical or political references that could distract from the experience.&lt;/p&gt;

&lt;p&gt;I kept the scope narrow on purpose.&lt;/p&gt;

&lt;p&gt;The project makes one promise and tries to deliver it well.&lt;/p&gt;

&lt;p&gt;I would rather make one interaction feel finished than ship five half-built modes.&lt;/p&gt;




&lt;h3&gt;
  
  
  Reliability by Design
&lt;/h3&gt;

&lt;p&gt;Tifo Forge is built to stay usable even when the Gemini free-tier quota is temporarily reached.&lt;/p&gt;

&lt;p&gt;The system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tries Gemini first&lt;/li&gt;
&lt;li&gt;validates every structured response&lt;/li&gt;
&lt;li&gt;caches successful Gemini plans&lt;/li&gt;
&lt;li&gt;falls back to a deterministic local design plan when needed&lt;/li&gt;
&lt;li&gt;labels the source clearly in the interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That way, a temporary API limit does not turn into a broken demo.&lt;/p&gt;

&lt;p&gt;I also constrained the renderer to a tested set of layouts and animations. Gemini still makes the creative decisions, but every result maps to a rendering path the application can handle cleanly.&lt;/p&gt;

&lt;p&gt;The tradeoff is less arbitrary visual freedom in exchange for a more reliable experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Google AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gemini is the creative-direction layer at the center of Tifo Forge. It translates the selected team, emotion, and symbol into the message, formation, motion, intensity, and design rationale that control the final reveal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;A tifo is never just an image.&lt;/p&gt;

&lt;p&gt;It is thousands of people agreeing to feel the same thing at the same time.&lt;/p&gt;

&lt;p&gt;Tifo Forge gives that feeling a message, a formation, and a moment to rise.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What should 50,000 supporters feel together?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the question behind the project.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Independent fan project. Not affiliated with or endorsed by FIFA.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Finishing a Tokyo Metro Data Pipeline: The Join I Refused to Fake</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Sun, 07 Jun 2026 05:56:47 +0000</pubDate>
      <link>https://dev.to/mneang/finishing-a-tokyo-metro-data-pipeline-the-join-i-refused-to-fake-5cdj</link>
      <guid>https://dev.to/mneang/finishing-a-tokyo-metro-data-pipeline-the-join-i-refused-to-fake-5cdj</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The most important part of my Tokyo Metro dashboard was not a chart.&lt;/p&gt;

&lt;p&gt;It was the join I refused to create.&lt;/p&gt;

&lt;p&gt;When I came back to this project, I already had useful pieces: station data, passenger averages, line names, revenue data, Japanese and English labels, and Python scripts that could clean some of it. But the project was stuck in the uncomfortable middle. The CSVs existed. The idea still made sense. Some scripts worked.&lt;/p&gt;

&lt;p&gt;But I would not have confidently handed the repo to another person.&lt;/p&gt;

&lt;p&gt;Not because the idea is bad, but because finishing the last 20% is not glamorous. It is field names, validation, weird edge cases, documentation, screenshots, and deciding what the data should not claim.&lt;/p&gt;

&lt;p&gt;The GitHub Finish-Up-A-Thon gave me a reason to stop leaving it half-finished. GitHub Copilot became useful once I treated it less like a generator and more like a reviewer for the parts I was most likely to rush.&lt;/p&gt;

&lt;p&gt;I revived the project into a small transit analytics workflow: Python cleaning scripts, cleaned CSV outputs, SQLite validation, SQL business queries, a documented data model, and a Japanese-first bilingual Tableau dashboard called:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;東京メトロ 需要・収益分析 / Tokyo Metro Demand &amp;amp; Revenue Analytics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mneang/tokyo-metro-data-pipeline" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://public.tableau.com/views/_17808045955120/sheet8?:language=en-US&amp;amp;:sid=&amp;amp;:redirect=auth&amp;amp;:display_count=n&amp;amp;:origin=viz_share_link" rel="noopener noreferrer"&gt;Interactive Tableau dashboard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe87j0jn4c4g3daylxvu1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe87j0jn4c4g3daylxvu1.jpeg" alt="Final dashboard: a Japanese-first bilingual view of revenue trends, passenger demand by line, and major station hubs" width="800" height="444"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Final dashboard: a Japanese-first bilingual view of revenue trends, passenger demand by line, and major station hubs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was not trying to present this as an official Tokyo Metro product. I wanted it to become a finished portfolio project that could survive basic questions:&lt;/p&gt;

&lt;p&gt;Where did the data go?&lt;br&gt;&lt;br&gt;
Which joins are valid?&lt;br&gt;&lt;br&gt;
What does the dashboard answer?&lt;br&gt;&lt;br&gt;
What are the limitations?&lt;/p&gt;

&lt;p&gt;My background is in business systems, reporting, data quality, and operational workflows, so I approached this less like a “make a cool chart” project and more like a small internal analytics deliverable. In that kind of work, a clean visual is not enough. The numbers need a path behind them.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;The finished version has a simple pipeline:&lt;/p&gt;

&lt;p&gt;Processed data&lt;br&gt;&lt;br&gt;
→ cleaned CSVs&lt;br&gt;&lt;br&gt;
→ SQLite database validation&lt;br&gt;&lt;br&gt;
→ SQL business queries&lt;br&gt;&lt;br&gt;
→ Tableau dashboard&lt;br&gt;&lt;br&gt;
→ README and visual documentation&lt;/p&gt;

&lt;p&gt;The major outputs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cleaned station, line, passenger, and revenue datasets&lt;/li&gt;
&lt;li&gt;a SQLite rebuild with row-count validation&lt;/li&gt;
&lt;li&gt;SQL queries for passenger demand, station hubs, and revenue trends&lt;/li&gt;
&lt;li&gt;an ERD and standalone revenue caveat&lt;/li&gt;
&lt;li&gt;a published Japanese-first bilingual Tableau dashboard&lt;/li&gt;
&lt;li&gt;a README that explains what the project does and what the data cannot claim&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final dashboard focuses on three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How is system-wide revenue trending by fiscal year and month?&lt;/li&gt;
&lt;li&gt;Which lines carry the highest passenger demand?&lt;/li&gt;
&lt;li&gt;Which stations stand out as major hubs?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I kept the interface Japanese-first because the subject is Tokyo Metro. English is included for accessibility, but Japanese leads the dashboard.&lt;/p&gt;

&lt;p&gt;I have visited Tokyo three times, and that made me more careful about the interface. I did not want the Japanese labels to feel decorative. I wanted them to lead the dashboard because that is the natural context of the data.&lt;/p&gt;

&lt;p&gt;I also kept the filters limited:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;会計年度 / Fiscal Year&lt;/strong&gt; for revenue views&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;路線 / Line&lt;/strong&gt; for passenger views&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I intentionally avoided adding every possible slicer. A dashboard does not become better just because it has more controls.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The final dashboard is a compact view of demand and revenue.&lt;/p&gt;

&lt;p&gt;It is designed around a smaller set of practical questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are revenue trends recovering or weakening?&lt;/li&gt;
&lt;li&gt;Which lines carry the most passenger demand?&lt;/li&gt;
&lt;li&gt;Which stations act as major hubs?&lt;/li&gt;
&lt;li&gt;What should the viewer not assume from this data?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dashboard includes a note that revenue is system-wide, while passenger data is station/line-based. That prevents the viewer from assuming the revenue data can be filtered by line or station.&lt;/p&gt;

&lt;p&gt;I skipped a map on purpose. The cleaned station data did not include reliable latitude and longitude fields. A map would have looked nice, but it would not have been honest.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;Before the finish-up work, this project had potential but no real finish line.&lt;/p&gt;

&lt;p&gt;The old version had a minimal README, cleaning scripts that needed polish, inconsistent field naming around revenue and fiscal month fields, no strong SQLite validation story, no clear data model explanation, and no published dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft712pzyan364nrl0a484.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft712pzyan364nrl0a484.png" alt="Before README screenshot showing the unfinished project state" width="799" height="545"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before: the project had the idea, but not enough documentation or finish.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The biggest fix was not visual.&lt;/p&gt;

&lt;p&gt;The biggest fix was deciding what had to be true before the dashboard deserved to exist.&lt;/p&gt;

&lt;p&gt;For this project, I treated “finished” as a checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can the cleaned files be regenerated?&lt;/li&gt;
&lt;li&gt;Can the database be rebuilt?&lt;/li&gt;
&lt;li&gt;Can the row counts be validated?&lt;/li&gt;
&lt;li&gt;Do the SQL queries answer real questions?&lt;/li&gt;
&lt;li&gt;Are the data relationships honest?&lt;/li&gt;
&lt;li&gt;Can someone open the dashboard without me explaining it live?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of jumping straight back into Tableau, I went through the cleaning scripts, the field names, the SQLite import, and the data model.&lt;/p&gt;

&lt;p&gt;One issue that forced the project to grow up was the station data. The Marunouchi Branch Line created a duplicate &lt;code&gt;Station_ID&lt;/code&gt; problem during SQLite loading. That was annoying at the time, but it was also useful. SQLite caught something Tableau might have let me hide visually.&lt;/p&gt;

&lt;p&gt;So I fixed the cleaning logic instead of working around the problem.&lt;/p&gt;

&lt;p&gt;After that cleanup, the database rebuild became a real proof point. The final import step loads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;14 rows into &lt;code&gt;Lines&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;291 rows into &lt;code&gt;Stations&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;119 rows into &lt;code&gt;Passengers&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;48 rows into &lt;code&gt;Revenue&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That terminal output became one of the most important screenshots in the project. It showed that the pipeline could rebuild from cleaned files instead of depending on a manual Tableau-only workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxlerpvkklgkd2jpo9li.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxlerpvkklgkd2jpo9li.png" alt="SQLite terminal output showing row count validation for Lines, Stations, Passengers, and Revenue" width="799" height="283"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The SQLite rebuild became the project’s validation checkpoint.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Data Modeling Decision That Mattered
&lt;/h2&gt;

&lt;p&gt;The most important modeling decision was deciding what &lt;strong&gt;not&lt;/strong&gt; to join.&lt;/p&gt;

&lt;p&gt;The passenger side of the project has a natural path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lines → Stations → Passengers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That lets me analyze passenger demand by line and by station.&lt;/p&gt;

&lt;p&gt;Revenue was different.&lt;/p&gt;

&lt;p&gt;The revenue table had fiscal year, fiscal month, commuter revenue, non-commuter revenue, total revenue, and YoY percentage fields. It did not have &lt;code&gt;Line_ID&lt;/code&gt; or &lt;code&gt;Station_ID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So I kept revenue standalone.&lt;/p&gt;

&lt;p&gt;That is not as exciting as adding another relationship in a diagram, but it is more honest. If I forced revenue into the station or line model, the dashboard would look more connected while becoming less trustworthy.&lt;/p&gt;

&lt;p&gt;I have seen enough reporting work to know that the dangerous chart is not always the broken one. Sometimes it is the clean chart built on a relationship that should not exist.&lt;/p&gt;

&lt;p&gt;That became the core lesson of this project:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not join data just because the dashboard would look more impressive.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fso417760ivu9rehg21yq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fso417760ivu9rehg21yq.png" alt="Revenue standalone data model graphic showing revenue is not joined to station or line tables" width="799" height="433"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Revenue remains standalone because the source does not include line or station keys.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;The most useful Copilot work happened after I already had something running.&lt;/p&gt;

&lt;p&gt;I did not ask it to build the whole project. I used it more like a second reviewer for the parts that are easy to rush when you are trying to finish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are the backend scripts readable?&lt;/li&gt;
&lt;li&gt;Are there obvious validation gaps?&lt;/li&gt;
&lt;li&gt;Are the comments and print messages clear?&lt;/li&gt;
&lt;li&gt;Are the SQL queries understandable to someone reviewing the project?&lt;/li&gt;
&lt;li&gt;Am I about to make a risky change for no real benefit?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best prompts were specific. For example, I asked Copilot to review &lt;code&gt;import_data_to_sqlite.py&lt;/code&gt;, &lt;code&gt;clean_station_data.py&lt;/code&gt;, and &lt;code&gt;create_line_data.py&lt;/code&gt; for clarity, validation, and maintainability while preserving the output schema.&lt;/p&gt;

&lt;p&gt;That last part mattered. When a project is close to finished, a large refactor can be a trap. I needed safe improvements, not a new project.&lt;/p&gt;

&lt;p&gt;Here is the kind of Copilot help that was actually useful: not “build the project for me,” but “review these scripts and point out what would make them safer to rerun.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvtz7u8oqbvg3b6q8z2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvtz7u8oqbvg3b6q8z2p.png" alt="GitHub Copilot review of backend scripts for validation and maintainability" width="799" height="530"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmxtkxc6ewa2q71zown6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmxtkxc6ewa2q71zown6.png" alt="GitHub Copilot review of backend scripts for validation and maintainability" width="800" height="555"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Copilot reviewing the backend scripts for validation and maintainability. I used it as a reviewer, not as an autopilot.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One concrete example was the SQLite import workflow. I had a script that could load the cleaned files, but Copilot’s review pushed me to think more like a maintainer: check expected inputs, preserve the schema, keep the output predictable, and make the final row counts visible.&lt;/p&gt;

&lt;p&gt;The most useful outcome was not a giant rewrite. It was a tighter import workflow: expected files checked, table schemas respected, and row counts printed at the end so the pipeline had a visible pass/fail moment.&lt;/p&gt;

&lt;p&gt;I still had to test the result myself. Copilot did not replace that. But the review helped turn the script from “works on my machine” into something I could actually show in the submission.&lt;/p&gt;

&lt;p&gt;I also did not accept every suggestion. The useful ones were the ones that made the pipeline easier to verify without changing the data model. Suggestions that felt like unnecessary refactoring or scope creep stayed out.&lt;/p&gt;

&lt;p&gt;The pattern that worked best was boring, but useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ask for review
→ keep the safe suggestions
→ reject the risky ones
→ rerun the pipeline
→ verify the output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is probably how I will use Copilot more often: not as a replacement for judgment, but as pressure against sloppy finish-up work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the SQL Layer Added
&lt;/h2&gt;

&lt;p&gt;Once the database loaded, I used SQL as a sanity check before going back to Tableau.&lt;/p&gt;

&lt;p&gt;The queries were not meant to be complicated. They were meant to answer questions a transit analyst or business reviewer would understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which lines carry the most passenger demand?&lt;/li&gt;
&lt;li&gt;Which stations act as major hubs?&lt;/li&gt;
&lt;li&gt;How has revenue changed by fiscal year?&lt;/li&gt;
&lt;li&gt;How do commuter and non-commuter revenue streams compare?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That helped me avoid treating Tableau as the only proof. The dashboard became the final interface, not the only place where the logic existed.&lt;/p&gt;

&lt;p&gt;This mattered because a dashboard can hide a lot. SQL forced me to look at the grain of the data before designing around it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6nbwl5nxl6o5fyf9hx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6nbwl5nxl6o5fyf9hx1.png" alt="SQL query result validating passenger demand logic" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;SQL checks helped validate business questions before building the Tableau view.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and After
&lt;/h2&gt;

&lt;p&gt;Here is the short version of the comeback:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;Minimal README&lt;/td&gt;
&lt;td&gt;Project page with links, screenshots, and caveats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data cleaning&lt;/td&gt;
&lt;td&gt;Scripts existed but needed cleanup&lt;/td&gt;
&lt;td&gt;Normalized cleaned datasets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;No strong validation checkpoint&lt;/td&gt;
&lt;td&gt;SQLite rebuild with row-count validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;td&gt;Exploratory or missing&lt;/td&gt;
&lt;td&gt;Business-focused queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data model&lt;/td&gt;
&lt;td&gt;Relationships not clearly explained&lt;/td&gt;
&lt;td&gt;ERD plus standalone revenue note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard&lt;/td&gt;
&lt;td&gt;Not finished&lt;/td&gt;
&lt;td&gt;Published bilingual Tableau dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot&lt;/td&gt;
&lt;td&gt;Not part of the workflow&lt;/td&gt;
&lt;td&gt;Used for review, validation, and polish&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That before-and-after is what led me to a checklist I will reuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Finish-Up Checklist for Data Projects
&lt;/h2&gt;

&lt;p&gt;Before calling a data project done, I want to be able to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can I regenerate the cleaned data?&lt;/li&gt;
&lt;li&gt;Can I rebuild the database or analysis layer?&lt;/li&gt;
&lt;li&gt;Can I validate row counts?&lt;/li&gt;
&lt;li&gt;Can I explain every relationship in the model?&lt;/li&gt;
&lt;li&gt;Can I explain what is intentionally not related?&lt;/li&gt;
&lt;li&gt;Can SQL answer the core business questions?&lt;/li&gt;
&lt;li&gt;Can the dashboard be used without me narrating it?&lt;/li&gt;
&lt;li&gt;Are the caveats visible enough that someone will not overread the data?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Would Improve Next
&lt;/h2&gt;

&lt;p&gt;If I had more time, I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automated tests for the cleaning scripts&lt;/li&gt;
&lt;li&gt;a GitHub Actions check that rebuilds the SQLite database&lt;/li&gt;
&lt;li&gt;clearer source-data documentation&lt;/li&gt;
&lt;li&gt;accurate latitude and longitude data before attempting a map&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I skipped a map on purpose. Without reliable latitude and longitude data, it would have made the project look better while making it less accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;For this repo, finishing meant cleaning the data, validating the load, documenting the model, refusing a bad join, and building a dashboard someone else could actually use.&lt;/p&gt;

&lt;p&gt;Copilot helped with the review and polish, but the main value came from deciding what “done” should mean.&lt;/p&gt;

&lt;p&gt;That is what changed this from an abandoned side project into a shareable analytics workflow.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>dataengineering</category>
      <category>sql</category>
    </item>
    <item>
      <title>Stop Asking Gemma 4 to Just Summarize</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Sun, 24 May 2026 01:34:58 +0000</pubDate>
      <link>https://dev.to/mneang/stop-asking-gemma-4-to-just-summarize-5e6</link>
      <guid>https://dev.to/mneang/stop-asking-gemma-4-to-just-summarize-5e6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Write About Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A small test of using open models to expose uncertainty, not hide it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most AI demos start with clean prompts.&lt;/p&gt;

&lt;p&gt;Real work usually starts with messy notes.&lt;/p&gt;

&lt;p&gt;A stakeholder says the number looks wrong. Someone else says a field changed. Another person says the source file was cleaned. The dashboard owner is out. A manager still needs an update before a leadership meeting.&lt;/p&gt;

&lt;p&gt;That is the kind of situation I wanted to test with Gemma 4.&lt;/p&gt;

&lt;p&gt;I work around business systems, reporting, data quality, and process handoffs, so this felt familiar. In real reporting work, the dangerous moment is not always when the dashboard breaks. It is when everyone wants an answer before anyone has checked the source.&lt;/p&gt;

&lt;p&gt;So I tested one practical question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can Gemma 4 do something more useful than summarize messy notes?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More specifically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can it separate the mess into what is known, what is assumed, what is risky, and what still needs checking?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the difference I cared about.&lt;/p&gt;

&lt;p&gt;A summary compresses messy information. A review packet separates it into facts, assumptions, risks, and next checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test setup
&lt;/h2&gt;

&lt;p&gt;I kept the test small on purpose.&lt;/p&gt;

&lt;p&gt;I used Google AI Studio with &lt;strong&gt;Gemma 4 26B A4B IT&lt;/strong&gt;. I used the same messy notes twice, with the same model settings: temperature &lt;code&gt;0.25&lt;/code&gt;, thinking level set to High, no tools, and no system instructions.&lt;/p&gt;

&lt;p&gt;I left system instructions blank because I wanted the prompt itself to carry the behavior.&lt;/p&gt;

&lt;p&gt;This was not a model comparison. It was a prompt-pattern comparison.&lt;/p&gt;

&lt;p&gt;The only thing I changed was the prompt.&lt;/p&gt;

&lt;p&gt;The scenario was synthetic, but realistic: a weekly operations report is due Monday morning, the totals look lower than expected, a field name changed last week, duplicate rows may have been removed, and the dashboard owner is out.&lt;/p&gt;

&lt;p&gt;No private data. No company data. Just the kind of messy report situation that creates pressure before a meeting.&lt;/p&gt;

&lt;p&gt;Here were the notes, shortened for the test:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A weekly operations report is due Monday morning.&lt;br&gt;&lt;br&gt;
The report usually refreshes Sunday night.&lt;br&gt;&lt;br&gt;
A field name changed last week.&lt;br&gt;&lt;br&gt;
A stakeholder says totals look lower than usual.&lt;br&gt;&lt;br&gt;
Duplicate rows may have been removed from the source file.&lt;br&gt;&lt;br&gt;
The dashboard owner is out.&lt;br&gt;&lt;br&gt;
Nobody knows yet whether the issue is source data, filter logic, or a definition change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prompt 1: ask for a normal summary
&lt;/h2&gt;

&lt;p&gt;First, I asked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summarize these notes for the manager and explain what is going on.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was not bad.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgknn95hcefx1lbt4kpkn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgknn95hcefx1lbt4kpkn.png" alt="Baseline summary output in Google AI Studio using Gemma 4 26B A4B IT" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Baseline run: same Gemma 4 model, same notes, no system instructions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Gemma 4 gave me a manager-ready update and a plain-English explanation of why the team should avoid giving final numbers yet. One useful line was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Investigation in progress regarding a discrepancy in the weekly totals.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I could imagine sending a version of that in Slack.&lt;/p&gt;

&lt;p&gt;But the summary also showed the weakness of summarization. It used phrases like “red alert,” “flying blind,” and “the numbers look wrong.”&lt;/p&gt;

&lt;p&gt;Those phrases made the situation easier to understand, but they also added more certainty and drama than the original notes supported.&lt;/p&gt;

&lt;p&gt;The raw notes did not prove the report was wrong. They only said the totals looked lower than usual.&lt;/p&gt;

&lt;p&gt;That matters.&lt;/p&gt;

&lt;p&gt;A summary can make messy information easier to read while quietly making the situation feel more settled than it actually is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt 2: ask for a review packet
&lt;/h2&gt;

&lt;p&gt;So I reran the same notes with one change: instead of asking for a summary, I asked for a review packet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an analyst preparing a review packet.

Using only the notes below, return:
1. One-paragraph summary
2. Confirmed facts
3. Assumptions
4. Unverified claims
5. Risks
6. Questions for a human reviewer
7. Suggested next actions
8. What not to conclude yet
9. Final checklist

Rules:
- Do not invent facts.
- Separate facts from assumptions.
- Keep it calm, practical, and concise.
- Help a human decide what to verify next; do not make the final decision.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output was less polished, but more useful for the decision in front of the team.&lt;/p&gt;

&lt;p&gt;It separated confirmed facts from assumptions and unverified claims. For example, it treated “totals look lower than usual” as an unverified claim because that came from a stakeholder report, not from a completed data check.&lt;/p&gt;

&lt;p&gt;It also kept the biggest unknown visible:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The root cause is currently unknown.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was the most important part of the output.&lt;/p&gt;

&lt;p&gt;In a real reporting issue, people often want to jump straight to the cause: the field change broke it, the source file changed it, the filter logic is wrong.&lt;/p&gt;

&lt;p&gt;But in this scenario, none of that had been verified yet.&lt;/p&gt;

&lt;p&gt;The strongest section was “What not to conclude yet”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not conclude that the data is incorrect.&lt;/li&gt;
&lt;li&gt;Do not conclude that the field name change or duplicate removal caused the discrepancy.&lt;/li&gt;
&lt;li&gt;Do not conclude whether the issue is source data, filter logic, or a definition change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That section changed the output from a status update into a review tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flidxtxd429jcvs16xm9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flidxtxd429jcvs16xm9n.png" alt="Review packet output in Google AI Studio separating confirmed facts, assumptions, unverified claims, and risks" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Review-packet run: the useful shift was separating facts, assumptions, risks, and what not to conclude yet.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0pwawxinlhmc60mx6cv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0pwawxinlhmc60mx6cv.png" alt="Summary vs Review Packet comparison" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The better output was not better because it sounded smarter.&lt;/p&gt;

&lt;p&gt;It was better because it gave the team a safer shape for the next step.&lt;/p&gt;

&lt;p&gt;The summary was better for status communication. The review packet was better for deciding what still needed verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The caution: this still does not verify anything
&lt;/h2&gt;

&lt;p&gt;This is the boundary I would not cross.&lt;/p&gt;

&lt;p&gt;Gemma 4 did not inspect the report. It did not open the source file. It did not check the dashboard logic. It did not know whether duplicate removal was correct or whether the field name change affected a calculation.&lt;/p&gt;

&lt;p&gt;It organized the review path.&lt;/p&gt;

&lt;p&gt;That is useful, but it is not the same as verification.&lt;/p&gt;

&lt;p&gt;In a real report issue, I would still check the source rows, field mapping, report filters, refresh timing, and metric definition before saying anything final. The review packet does not remove that work. It makes sure I do not skip it.&lt;/p&gt;

&lt;p&gt;For messy systems work, I would use this kind of output before the investigation, not after it. It is a way to prepare the human reviewer, not replace them.&lt;/p&gt;

&lt;p&gt;The model can turn scattered notes into a checklist for the person doing the investigation.&lt;/p&gt;

&lt;p&gt;It cannot do the investigation unless it has access to the actual systems, data, definitions, and business context.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Review Packet Framework
&lt;/h2&gt;

&lt;p&gt;If someone only copied one thing from this post, I would want it to be this prompt shape.&lt;/p&gt;

&lt;p&gt;When the input is messy, uncertain, or time-sensitive, ask the model for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;Confirmed facts&lt;/li&gt;
&lt;li&gt;Assumptions&lt;/li&gt;
&lt;li&gt;Unverified claims&lt;/li&gt;
&lt;li&gt;Risks&lt;/li&gt;
&lt;li&gt;Questions for a human reviewer&lt;/li&gt;
&lt;li&gt;Suggested next actions&lt;/li&gt;
&lt;li&gt;What not to conclude yet&lt;/li&gt;
&lt;li&gt;Final checklist&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most useful parts are not always the summary or the checklist.&lt;/p&gt;

&lt;p&gt;For me, the important sections are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;assumptions&lt;/li&gt;
&lt;li&gt;unverified claims&lt;/li&gt;
&lt;li&gt;risks&lt;/li&gt;
&lt;li&gt;what not to conclude yet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those sections force the model to keep uncertainty visible instead of hiding it inside a clean paragraph.&lt;/p&gt;

&lt;p&gt;That is what made the review packet more useful than the summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I used Gemma 4 this way
&lt;/h2&gt;

&lt;p&gt;I used Gemma 4 this way because messy notes are a real workload.&lt;/p&gt;

&lt;p&gt;Reports, support handoffs, incident updates, and project notes rarely arrive in perfect form. The useful question is not only whether a model can write about them. It is whether it can preserve the uncertainty inside them.&lt;/p&gt;

&lt;p&gt;That fits the part of Gemma 4 I care about most: not just text generation, but reasoning over messy context and producing something structured enough for a human to use.&lt;/p&gt;

&lt;p&gt;For this test, I used the 26B A4B instruction model because I cared more about the reasoning structure than running the smallest possible model. If I were building a lightweight offline helper, I would test a smaller Gemma 4 model next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;I would still ask Gemma 4 for summaries.&lt;/p&gt;

&lt;p&gt;But after this test, I would be more careful about when a summary is enough.&lt;/p&gt;

&lt;p&gt;If the situation is messy, time-sensitive, or full of unverified claims, I would rather ask for a review packet.&lt;/p&gt;

&lt;p&gt;The useful output is not always the cleanest paragraph. Sometimes it is the output that says:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;here is what we know&lt;/li&gt;
&lt;li&gt;here is what we are assuming&lt;/li&gt;
&lt;li&gt;here is what could go wrong&lt;/li&gt;
&lt;li&gt;here is what not to conclude yet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the pattern I would reuse.&lt;/p&gt;

&lt;p&gt;Gemma 4 did not remove the mess.&lt;/p&gt;

&lt;p&gt;It made the mess reviewable.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
      <category>ai</category>
    </item>
    <item>
      <title>When the Dashboard Lies: The Google I/O 2026 Agent Test</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Sat, 23 May 2026 21:54:22 +0000</pubDate>
      <link>https://dev.to/mneang/when-the-dashboard-lies-the-google-io-2026-agent-test-39lf</link>
      <guid>https://dev.to/mneang/when-the-dashboard-lies-the-google-io-2026-agent-test-39lf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-io-writing-2026-05-19"&gt;Google I/O Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is Monday morning. A leadership meeting starts in an hour. The sales dashboard is open, the numbers look normal, and nobody is seeing a big red error message.&lt;/p&gt;

&lt;p&gt;Then someone says the worst possible sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I don’t think this number is right.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is when a dashboard stops being a dashboard and becomes an investigation.&lt;/p&gt;

&lt;p&gt;Maybe the refresh timestamp looks fine, but the source data is behind. Maybe the source system has the new records, but the report is filtering them out. Maybe the pipeline technically completed, but skipped something important. Or maybe the number is correct and the business definition changed without everyone realizing it.&lt;/p&gt;

&lt;p&gt;That is the kind of problem I thought about during Google I/O 2026.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.googleblog.com/all-the-news-from-the-google-io-2026-developer-keynote/" rel="noopener noreferrer"&gt;Google I/O had plenty of bigger moments&lt;/a&gt;: new Gemini models, Antigravity, managed agents, AI Studio updates, Firebase improvements, and more ways to build with agents. But the part that stayed with me was not just that agents could do more. It was that Google kept showing the pieces around the agent: tools, sandboxed execution, deployment paths, realtime data, and approval moments.&lt;/p&gt;

&lt;p&gt;That matters for business systems.&lt;/p&gt;

&lt;p&gt;An agent is not useful because it sounds smart. It is useful if the surrounding workflow helps it check, explain, and stop at the right time.&lt;/p&gt;

&lt;p&gt;For me, the test is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can an agent help investigate a broken dashboard before people trust the wrong answer?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  My lens is business systems
&lt;/h2&gt;

&lt;p&gt;My lens is not pure software engineering. I work around business systems: reporting, Salesforce-style processes, data quality, workflow rules, and the awkward handoff between “the system says this” and “the business expected that.”&lt;/p&gt;

&lt;p&gt;So when I watch agent demos, I do not only ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Can this write code?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Could this help figure out why a number is wrong without making the situation worse?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In reporting work, I have learned to be careful with the phrase “the number is wrong.” Sometimes it means the number is stale. Sometimes it means the filter is wrong. Sometimes it means the user expected a different definition. Sometimes the data is correct, but the timing is not.&lt;/p&gt;

&lt;p&gt;Those are different problems. Treating them like one problem wastes time.&lt;/p&gt;

&lt;p&gt;A green checkmark on a refresh history page does not always mean the business problem is solved. A dashboard can be “working” and still be misleading.&lt;/p&gt;

&lt;p&gt;That is why a dashboard incident is a good test for AI agents. It forces them to handle the parts of work that demos usually skip: stale data, unclear definitions, permissions, uncertainty, and stakeholder communication.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small example: the $1.2M problem
&lt;/h2&gt;

&lt;p&gt;Imagine a sales dashboard says this month’s booked revenue is &lt;strong&gt;$1.2M&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sales says three new closed-won opportunities came in this morning, so the number should be higher. The dashboard refresh history says the report ran at &lt;strong&gt;8:10 AM&lt;/strong&gt;. The source system shows the new opportunities were updated at &lt;strong&gt;8:26 AM&lt;/strong&gt;. The pipeline status says the latest run completed, but with warnings.&lt;/p&gt;

&lt;p&gt;Nothing is obviously on fire.&lt;/p&gt;

&lt;p&gt;The dashboard loads.  &lt;/p&gt;

&lt;p&gt;The refresh did happen.  &lt;/p&gt;

&lt;p&gt;The source system has records.  &lt;/p&gt;

&lt;p&gt;The meeting still starts in an hour.&lt;/p&gt;

&lt;p&gt;This is exactly where vague confidence is dangerous. You do not need someone to say, “Looks fine.” You need someone to narrow the problem.&lt;/p&gt;

&lt;p&gt;Is the dashboard stale? Did the source update after the refresh? Did the pipeline skip some records? Is the report filtering out the new opportunities? Should leadership use the current number or wait for an updated one?&lt;/p&gt;

&lt;p&gt;That is the broken dashboard test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftylcouk7w8vadr9t9zpa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftylcouk7w8vadr9t9zpa.png" alt="The Broken Dashboard Test: problem, investigation, evidence, and human approval" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The manual version
&lt;/h2&gt;

&lt;p&gt;In real life, these checks rarely happen in a perfect order. You bounce around. You open the dashboard, then the source system, then a refresh history page, then maybe a spreadsheet someone sent last week.&lt;/p&gt;

&lt;p&gt;Half the work is technical. The other half is figuring out what people actually mean when they say “wrong.”&lt;/p&gt;

&lt;p&gt;A basic dashboard investigation 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;Step&lt;/th&gt;
&lt;th&gt;Manual analyst check&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;Check the dashboard refresh timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Check whether the source system has newer records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Check if the pipeline, export, or sync completed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Compare row counts, timestamps, or sample records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Look for filters, joins, or definitions that could explain the gap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Identify which reports, teams, or decisions may be affected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Write a plain-English update for stakeholders&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The hard part is switching between tools and contexts while people are waiting. You are trying to separate facts from guesses. You are also trying not to create more panic than necessary.&lt;/p&gt;

&lt;p&gt;That is where the Google I/O agent story became useful to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent version should be boring on purpose
&lt;/h2&gt;

&lt;p&gt;The part of Google I/O 2026 that clicked for me was the move away from “ask a model a question” toward “give an agent a job, tools, and boundaries.”&lt;/p&gt;

&lt;p&gt;A model can help explain what a stale dashboard might mean. An agent, designed carefully, could help check the refresh time, inspect a pipeline status, compare a few sample records, and produce a short recovery note for the humans involved.&lt;/p&gt;

&lt;p&gt;But the agent version should not be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Let the AI fix it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is how you get a bigger problem.&lt;/p&gt;

&lt;p&gt;The useful version is more controlled: let the agent organize the investigation, gather evidence, and prepare a recommendation. Then a person decides what to do.&lt;/p&gt;

&lt;p&gt;For a dashboard incident, I would want an agent to summarize the issue, identify the systems involved, gather freshness evidence, compare source records against dashboard output, estimate the blast radius, draft a recovery brief, and ask for approval before any risky action.&lt;/p&gt;

&lt;p&gt;That last point is not decoration. It is the line between useful automation and reckless automation.&lt;/p&gt;

&lt;p&gt;If an agent finds a failed refresh, it can recommend a rerun. If it sees a mismatch, it can flag the likely cause. If it drafts a message, great. But changing production data, editing report logic, disabling a process, or triggering a major workflow should require a human decision.&lt;/p&gt;

&lt;p&gt;The goal is faster evidence, not blind autonomy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Firebase demo mattered to me
&lt;/h2&gt;

&lt;p&gt;One I/O moment that fit this idea surprisingly well was a Firebase SQL Connect realtime demo. The demo itself was playful, but the pattern was familiar: data changed, the app did not immediately show the change, and then realtime sync improved how updates reached the user.&lt;/p&gt;

&lt;p&gt;That is basically the dashboard problem in miniature.&lt;/p&gt;

&lt;p&gt;Most business users will not describe the issue as a sync problem, a refresh issue, or a data modeling mismatch. They will just say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This looks wrong.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good agent workflow should help translate that vague sentence into checks that can actually be tested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When did the dashboard last refresh?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Did the source system receive newer records?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Did the pipeline run fully or only partially?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are the missing records actually missing, or filtered out?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Who needs to be warned before the meeting?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The value is in turning a vague complaint into checks someone can review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The recovery brief matters too
&lt;/h2&gt;

&lt;p&gt;The investigation is only half the job; the other half is explaining it without dumping technical noise on people who just need to know whether they can trust the number.&lt;/p&gt;

&lt;p&gt;A useful recovery brief should explain what happened, what is confirmed, what is still uncertain, who may be affected, what action is recommended, and when the next update will come.&lt;/p&gt;

&lt;p&gt;That sounds simple, but under pressure it is easy to write too much, too little, or the wrong thing entirely.&lt;/p&gt;

&lt;p&gt;This is an underrated agent use case. If the agent can gather evidence and draft a calm first version, the analyst can spend more time judging the situation instead of starting from a blank message.&lt;/p&gt;

&lt;p&gt;It gives a person something to review.&lt;/p&gt;

&lt;h2&gt;
  
  
  My caution: investigation and action are different jobs
&lt;/h2&gt;

&lt;p&gt;This is where I get cautious.&lt;/p&gt;

&lt;p&gt;A dashboard investigation agent should not have the same permissions as an admin. It should not be able to quietly rewrite report logic, change production data, or trigger a workflow just because it found something suspicious.&lt;/p&gt;

&lt;p&gt;Investigation and action are different jobs.&lt;/p&gt;

&lt;p&gt;I do not want an agent with admin-level access just because it makes the demo smoother. Convenience is not a permission model.&lt;/p&gt;

&lt;p&gt;For this kind of workflow, I would rather start with a read-only agent that can collect evidence, summarize findings, and recommend next steps. If a write action is needed, make the approval explicit.&lt;/p&gt;

&lt;p&gt;Read-only first is not a limitation. It is how trust gets built.&lt;/p&gt;

&lt;p&gt;Before putting an agent anywhere near a business systems workflow, I would ask these questions first:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Is the workflow repeatable?&lt;/td&gt;
&lt;td&gt;Agents work better when the process has structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Are the systems clearly named?&lt;/td&gt;
&lt;td&gt;Vague tools create vague investigations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can the evidence be verified?&lt;/td&gt;
&lt;td&gt;Confidence is not the same as proof&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Are permissions scoped?&lt;/td&gt;
&lt;td&gt;The agent should not reach everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Are write actions approval-gated?&lt;/td&gt;
&lt;td&gt;Investigation and action need different levels of trust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is there an audit trail?&lt;/td&gt;
&lt;td&gt;People need to understand what happened later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can a non-technical stakeholder understand the output?&lt;/td&gt;
&lt;td&gt;Raw logs are not a recovery update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is there an escalation path?&lt;/td&gt;
&lt;td&gt;Some problems should leave the agent’s hands&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The worst version of this agent would be one that confidently guesses. The best version would be one that carefully narrows the search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;My biggest Google I/O 2026 takeaway is not “agents are powerful.” We already know that.&lt;/p&gt;

&lt;p&gt;The harder question is whether they can be useful when the situation is messy, time-sensitive, and full of half-truths.&lt;/p&gt;

&lt;p&gt;For me, the test is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can the agent help move from panic to evidence?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If a dashboard lies, I do not need an agent that sounds confident. I need one that checks the right places, shows what it found, explains what is still uncertain, and stops before doing something risky.&lt;/p&gt;

&lt;p&gt;That is the agent I would trust: not the one that replaces judgment, but the one that helps people reach evidence before decisions get made.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleiochallenge</category>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>Everyone Wants AI Agents. Fewer Teams Are Ready for the Messy Business Context Behind Them</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Wed, 29 Apr 2026 02:56:02 +0000</pubDate>
      <link>https://dev.to/mneang/everyone-wants-ai-agents-fewer-teams-are-ready-for-the-messy-business-context-behind-them-ml6</link>
      <guid>https://dev.to/mneang/everyone-wants-ai-agents-fewer-teams-are-ready-for-the-messy-business-context-behind-them-ml6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-cloud-next-2026-04-22"&gt;Google Cloud NEXT Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I work as a Business Systems Data Analyst, mostly around Salesforce administration, Power BI reporting, and business process support. So when I watched the Google Cloud NEXT ’26 keynotes, the part that stood out to me was not only the models, infrastructure, or demos.&lt;/p&gt;

&lt;p&gt;It was the operational reality behind them.&lt;/p&gt;

&lt;p&gt;I am not a cloud architect, but I work close enough to Salesforce records, reports, and business processes to know that clean demos are different from real operations.&lt;/p&gt;

&lt;p&gt;One line from the Opening Keynote caught my attention:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You have moved beyond the pilot. The experimenting phase is behind us.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence made the keynote feel different. It was not just about proving AI can work anymore. It was about what happens when AI has to work inside real companies, with real data, real permissions, real users, and real consequences.&lt;/p&gt;

&lt;p&gt;That is where the agentic enterprise actually clicked for me.&lt;/p&gt;

&lt;p&gt;Because the hard part is rarely the perfect demo. The hard part is the messy workflow after the demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real shift is not chat. It is action.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr8ktioc0d17f9ahzudg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr8ktioc0d17f9ahzudg.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a long time, business technology has helped people understand what already happened.&lt;/p&gt;

&lt;p&gt;A dashboard shows performance. A report shows trends. A validation rule blocks bad data. A workflow moves a process forward.&lt;/p&gt;

&lt;p&gt;Agents introduce a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if the system does not only show information, but helps someone decide what to do next?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A dashboard might show that inventory is stale, a deal is stuck, or a support queue is growing. An agent could help investigate why, check related records, suggest the next action, draft a response, create a task, or hand work off to another system.&lt;/p&gt;

&lt;p&gt;That is exciting, but it also raises the standard.&lt;/p&gt;

&lt;p&gt;If an agent is going to assist with real work, it cannot behave like a random chatbot. It needs to understand the workflow, the data, the rules, and the boundaries of what it is allowed to do.&lt;/p&gt;

&lt;p&gt;I do not see the best use of agents as replacing people outright. I see them more as operational assistants that need onboarding, boundaries, and review.&lt;/p&gt;

&lt;p&gt;A new employee does not get access to everything on day one. They need context. They need process knowledge. They need someone to explain what “done” actually means.&lt;/p&gt;

&lt;p&gt;Agents are not that different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context is the difference between useful and dangerous
&lt;/h2&gt;

&lt;p&gt;The strongest line from the keynote, for me, was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Reasoning without context is just a guess.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the line I kept coming back to.&lt;/p&gt;

&lt;p&gt;An AI model can be powerful, but if it does not understand the business meaning behind the data, it can still make the wrong recommendation with confidence.&lt;/p&gt;

&lt;p&gt;In a company, words like revenue, risk, status, approval, customer, inventory, or complete are not universal. They depend on systems, departments, rules, and history.&lt;/p&gt;

&lt;p&gt;“Risk” could mean credit risk, operational risk, compliance risk, fraud risk, or customer churn risk.&lt;/p&gt;

&lt;p&gt;“Revenue” could mean booked revenue, net revenue, recognized revenue, projected revenue, or something else depending on the report.&lt;/p&gt;

&lt;p&gt;Even a simple status field can carry a lot of business meaning behind it.&lt;/p&gt;

&lt;p&gt;That is why the Agentic Data Cloud and Knowledge Catalog parts of the keynote stood out to me. The interesting part was not only that data can be stored or searched. It was the idea that agents need trusted business context to act correctly.&lt;/p&gt;

&lt;p&gt;Without that context, an agent can sound impressive and still be wrong.&lt;/p&gt;

&lt;p&gt;My biggest takeaway from NEXT ’26 is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The future of agents is not only about smarter models. It is about better context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Good agent systems look more like teams
&lt;/h2&gt;

&lt;p&gt;The Developer Keynote made the agent platform feel practical.&lt;/p&gt;

&lt;p&gt;The marathon planning demo used multiple agents: a Planner Agent, an Evaluator Subagent, and a Simulator Agent. That stood out because a useful agent system is not necessarily one giant chatbot trying to do everything.&lt;/p&gt;

&lt;p&gt;It is more like a team.&lt;/p&gt;

&lt;p&gt;One agent creates the plan. Another evaluates it. Another simulates what could happen.&lt;/p&gt;

&lt;p&gt;That model makes sense because real business workflows already operate like this. Work moves through roles, checks, approvals, handoffs, and reviews. One person or one system usually does not own everything from start to finish.&lt;/p&gt;

&lt;p&gt;The demo also reminded me that agents are still software.&lt;/p&gt;

&lt;p&gt;They may feel more flexible than traditional applications, but they still need testing, monitoring, permissions, debugging, and maintenance. If anything, they may need even more discipline because they can reason, call tools, and take action in less predictable ways.&lt;/p&gt;

&lt;p&gt;That is not a reason to avoid agents. It is a reason to build them seriously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action needs guardrails
&lt;/h2&gt;

&lt;p&gt;The most exciting shift is that agents are moving from answering questions to taking action.&lt;/p&gt;

&lt;p&gt;But action increases risk.&lt;/p&gt;

&lt;p&gt;An agent that only answers questions has limited impact. An agent that can take action has real value. An agent that can take action without guardrails can create real problems.&lt;/p&gt;

&lt;p&gt;That is why least privilege stood out to me. Agents should access only what they need, only when they need it. An agent should not automatically have access to every system or every data point just because access might be useful.&lt;/p&gt;

&lt;p&gt;If an agent can update financial data, send customer communication, change a workflow, trigger a deployment, or access sensitive records, then the organization needs to know exactly what that agent is allowed to do.&lt;/p&gt;

&lt;p&gt;It also needs to be traceable.&lt;/p&gt;

&lt;p&gt;Who or what took the action? What data did it use? What tool did it call? Was a human involved? Can the decision be reviewed later?&lt;/p&gt;

&lt;p&gt;This is where governance becomes more than a compliance checkbox. It becomes part of whether people trust the system enough to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  My checklist before trusting an enterprise agent
&lt;/h2&gt;

&lt;p&gt;Before trusting an enterprise agent in a real workflow, I would ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What data can it access?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What action can it take?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What business rule is it following?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Who reviews the output?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can we trace what it did?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What happens when it is wrong?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good enterprise agent should not only be impressive. It should be understandable, useful, controlled, and accountable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Google Cloud NEXT ’26 made the agentic enterprise feel more real to me.&lt;/p&gt;

&lt;p&gt;Not because agents can answer questions. We already know AI can answer questions.&lt;/p&gt;

&lt;p&gt;It felt real because the conversation moved toward the harder parts: production workflows, trusted context, governance, evaluation, observability, security, and action with guardrails.&lt;/p&gt;

&lt;p&gt;The companies that win with agents will not only be the ones with the smartest models. They will be the ones with clean data, clear processes, strong governance, and people who understand the business context.&lt;/p&gt;

&lt;p&gt;To me, that is the real message of the agentic enterprise: not replacing work, but helping people act with better context, better systems, and more confidence.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>cloudnextchallenge</category>
      <category>googlecloud</category>
      <category>ai</category>
    </item>
    <item>
      <title>IntelSync: Multi-Agent Market Intelligence with Google ADK &amp; Google Cloud</title>
      <dc:creator>Michael Neang</dc:creator>
      <pubDate>Mon, 23 Jun 2025 16:56:20 +0000</pubDate>
      <link>https://dev.to/mneang/intelsync-multi-agent-market-intelligence-with-google-adk-google-cloud-3g0</link>
      <guid>https://dev.to/mneang/intelsync-multi-agent-market-intelligence-with-google-adk-google-cloud-3g0</guid>
      <description>&lt;h1&gt;
  
  
  IntelSync: Building Multi-Agent AI Systems with Google ADK &amp;amp; Google Cloud
&lt;/h1&gt;

&lt;p&gt;Created for the purposes of entering the Google Cloud Agent Development Kit Hackathon. #adkhackathon&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Project Overview
&lt;/h2&gt;

&lt;p&gt;With AI agents on the rise, the true potential emerges when they collaborate. IntelSync leverages Google’s Agent Development Kit (ADK) to orchestrate multiple autonomous agents, streamlining complex business intelligence tasks through real-time news scraping, seamless integration with BigQuery, and instant, actionable insights via Google Cloud NLP APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Technologies &amp;amp; Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud BigQuery:&lt;/strong&gt; For scalable data warehousing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud Natural Language API:&lt;/strong&gt; Real-time sentiment analysis and content insights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent Development Kit (ADK)&lt;/strong&gt;: Robust orchestration and execution of multi-agent workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamlit:&lt;/strong&gt; Intuitive real-time dashboarding and visualization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛠 How I Built It
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent Orchestration with ADK:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Used Google's ADK to orchestrate distinct agents (web scraping, data loading, NLP insights) working autonomously yet cohesively.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google Cloud Integrations:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Integrated seamlessly with BigQuery and Natural Language API, enhancing the agent workflow's scalability and intelligence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-time Visualization:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Leveraged Streamlit for an intuitive and interactive dashboard providing insights at a glance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🏗 Technology Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsxxgesqzla1xc1hss5m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsxxgesqzla1xc1hss5m.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Findings &amp;amp; Impact
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Successfully automated complex multi-step data workflows.&lt;/li&gt;
&lt;li&gt;Demonstrated practical use cases for multi-agent collaboration.&lt;/li&gt;
&lt;li&gt;Explored real-world applications for autonomous insights generation and data-driven decision-making.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎓 Learnings
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Effective orchestration of AI agents significantly enhances business intelligence processes.&lt;/li&gt;
&lt;li&gt;Google's ADK simplifies multi-agent management, making complex AI workflows accessible and powerful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔗 Project Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://intelsync-adkhackathon.streamlit.app/" rel="noopener noreferrer"&gt;&lt;strong&gt;Live Demo&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mneang/intelsync" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/sX8--BPEt3E?si=dqvqfZfPgPagbopF" rel="noopener noreferrer"&gt;&lt;strong&gt;Video Demo&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌟 Acknowledgements
&lt;/h2&gt;

&lt;p&gt;Thanks to Google Cloud and the ADK team for providing such an exciting platform and opportunity to innovate with multi-agent systems.&lt;/p&gt;

&lt;p&gt;#adkhackathon #GoogleCloud #ADK #AI #DataAnalytics&lt;/p&gt;

</description>
      <category>adkhackathon</category>
      <category>googlecloud</category>
      <category>agentdevelopmentkit</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
