<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Kamesh Sampath on Medium]]></title>
        <description><![CDATA[Stories by Kamesh Sampath on Medium]]></description>
        <link>https://medium.com/@kameshsampath?source=rss-b2156b61bbf6------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*xM2XHSS0mO5UCW1XCtXu5Q.jpeg</url>
            <title>Stories by Kamesh Sampath on Medium</title>
            <link>https://medium.com/@kameshsampath?source=rss-b2156b61bbf6------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 13 Jul 2026 22:51:51 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kameshsampath/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Teaching Agents When Not to Act: Agentic DevOps with Snowflake CoCo]]></title>
            <link>https://kameshsampath.medium.com/teaching-agents-when-not-to-act-agentic-devops-with-snowflake-coco-4bc8f38d61ba?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/4bc8f38d61ba</guid>
            <category><![CDATA[snowflake]]></category>
            <category><![CDATA[autonomous-agent]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Fri, 03 Jul 2026 06:17:16 GMT</pubDate>
            <atom:updated>2026-07-03T06:17:16.723Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Why the future of DevOps isn’t just about what AI can do, but what it’s authorized to do.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dvT_Y_XLIoJX3ou0QqKzIg.png" /><figcaption>Agentic DevOps is not about teaching agents to act faster. It is about teaching them when not to act.</figcaption></figure><p>Like most engineers, I’ve had moments where I trusted automation a little more than I should have.</p><p>One of those moments came when I set up an auto-fix bot for a CI pipeline.</p><p>It found a race condition and “fixed” it by removing the lock. The deadlock that followed made its way into production and surfaced at 2am on a Friday.</p><p>The AI wasn’t really the problem. It did exactly what I asked it to do.</p><p>The problem was that I never defined what a safe fix looked like.</p><p>And the agent had no way to challenge that assumption.</p><h3>AI-assisted DevOps is not Agentic DevOps</h3><p>People use “AI-assisted DevOps” and “Agentic DevOps” interchangeably.</p><p>They are not the same thing.</p><p><strong>AI-assisted DevOps</strong> is a text generator with a human execution layer.</p><ul><li>Copilot suggests. You run.</li><li>Claude Code proposes. You accept.</li><li>The agent generates. The human ships.</li></ul><p><strong>Agentic DevOps</strong> is an execution model.</p><ul><li>Agent scaffolds infrastructure.</li><li>Provisions identities.</li><li>Scans code, opens issues, proposes fixes.</li><li>Cleans up when the work is complete.</li></ul><blockquote>You state the outcome. The agent executes the workflow.</blockquote><p>That is the pattern I have been exploring with<a href="https://www.snowflake.com/en/product/snowflake-coco/"> Snowflake CoCo</a>.</p><p>CoCo gives me an AI-native execution layer. The CI/CD workflow described here is an implementation pattern:</p><ul><li>Use intent to define the desired outcome</li><li>Use policy to define the agent’s authority</li><li>Use human review when the agent is not confident enough to act.</li></ul><p>The important part is not the repository.</p><p>The important part is the operating model.</p><h3>What changed when I applied IDD to CI/CD</h3><p>I have written before about <a href="https://blogs.kameshs.dev/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c">Intent-Driven Development</a>. The idea is simple: structure prompts around outcomes instead of procedural steps.</p><p>The four blocks are:</p><ul><li>Goal</li><li>Requirements</li><li>Constraints</li><li>Output</li></ul><p>The agent figures out how.</p><p>You define the what and the why.</p><p>When I started building this CI scaffold, the scan prompt initially said something like:</p><blockquote>Find bugs and open issues</blockquote><p>Simple. Direct. Not enough.</p><p>The agent found bugs.</p><p>It opened issues.</p><p>It also tried to auto-fix a 400-line authentication module with high confidence and wrong assumptions.</p><p>The problem was the missing constraint.</p><blockquote>“Fix my bugs” carries an implicit constraint : “Do it safely.”</blockquote><p>IDD forces that constraint into the prompt and the workflow.</p><p>Not as a comment.</p><p>Not as a README note.</p><p>As a policy that the agent reads before acting.</p><h3>The Decision Tree</h3><p>The constraint became a scoring system.</p><p>Before opening any issue or proposing a fix, the scan agent evaluates three dimensions.</p><ul><li><strong>SEVERITY</strong>: how dangerous the issue is if left unfixed.</li><li><strong>COMPLEXITY</strong>: how much the fix touches.</li><li><strong>CONFIDENCE</strong>: how certain the agent is that its fix is correct.</li></ul><p>Then it routes the issue.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/390/1*ZoI9txSNzrbnplt7qc9Icg.png" /><figcaption>The governance loop: Mapping an agent’s confidence against its authorized ceiling of action to determine when a human must step in.</figcaption></figure><h4><strong>conservative (default)</strong></h4><p>auto-fix only if severity=low AND complexity=low AND confidence=high everything else -&gt; needs-review</p><h4><strong>aggressive</strong></h4><p>auto-fix if confidence &gt;= medium</p><h4><strong>off</strong></h4><p>never auto-fix, always needs-review</p><p>This is not a magic rules engine.</p><p>It is a simple judgment model with explicit inputs.</p><p>The race-condition fix from my Friday story would have scored:</p><pre>severity: high<br>complexity: high<br>confidence: medium<br>route: needs-review</pre><p>A human would have seen the issue, read the context, and decided whether to run the fix.</p><p>That is the difference I care about.</p><p>An agent that always acts is dangerous.</p><p>An agent that never acts is just autocomplete with a workflow file.</p><p>The useful middle is an agent that can decide when not to act.</p><h3>Intent defines the goal. Policy defines the boundary.</h3><p><a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14">Infrastructure-as-Intent</a> does not mean the agent gets unlimited freedom.</p><p>It means the desired outcome is expressed clearly enough for the system to act.</p><p>But action still needs a boundary.</p><p>For this workflow, that boundary lives in configuration.</p><p>For example a policy document could be:</p><pre>fix_mode:<br>  max_auto: conservative</pre><p>The intent says what the agent should achieve.</p><p>The policy says how far the agent is allowed to go without human review.</p><p>Change it through a PR.</p><p>The commit history becomes the governance record.</p><p>When your security team asks who allowed the agent to auto-fix beyond low-risk changes, you do not need to reconstruct a Slack thread.</p><p>You show the PR that changed <strong>conservative</strong> to <strong>aggressive</strong>, the reviewer, and the merge date.</p><p>That is the record I want Agentic DevOps systems to preserve.</p><p>Not just what the agent did.</p><p>Who allowed it to act at that level.</p><p>For runtime experiments, there can still be a variable override.</p><p>Runtime overrides are necessary, but they introduce drift. Every temporary experiment requires a sunset tag and a hard expiration date. If the override outlives its sprint, the agent must revert to the version-controlled default and trigger a CI alert. This is how we ensure ‘temporary’ hacks don’t calcify into unreviewed permanent policy.</p><p>Maybe the team wants to try aggressive mode for a sprint. Set the variable. When the experiment ends, delete it. The policy falls back to the reviewed default.</p><p>Every scan run should log the active ceiling and its source.</p><pre>Fix ceiling: conservative<br>Source: repository configuration</pre><p>The point is not to slow the agent down.</p><p>To make authority visible, embed the active ceiling status directly into your PR templates. Don’t just show the <em>outcome</em> of the agent’s work; show the <em>governance state</em> that authorized it. A persistent ‘Governance Status’ block is the audit evidence your security team actually wants.</p><p>This is the engine of CoCo Automations. As agents move from reactive tasks to proactive operations — like daily drift detection — the ‘conservative’ vs. ‘aggressive’ dial becomes our primary safety check. Setting these modes at the repository level ensures that even when the agent operates autonomously, it is tethered to a pre-approved ceiling of authority.</p><blockquote>Visibility alone, however, is not a substitute for intervention.</blockquote><h3>Human override should always exist</h3><p>The needs-review route is not a dead end.</p><p>It is the place where human judgment enters the workflow.</p><p>A developer can review the issue, inspect the context, and decide whether the agent should proceed with the fix.</p><p>Even for a complex issue.</p><p>Even for a low-confidence issue.</p><p>The override exists by design.</p><p>Agent judgment is the default.</p><p>Human judgment is the final decision.</p><blockquote>agent proposes <strong>-&gt;</strong> human decides <strong>-&gt;</strong> agent executes</blockquote><p>A system where the agent can never be overridden is broken.</p><p>A system where the agent always acts is also broken.</p><p>The override is the acknowledgement that developers know their codebases better than any model.</p><p>They should always have the last word.</p><p>This is where Agentic DevOps becomes useful.</p><p>The agent is not replacing the engineer. It is doing the execution work after the engineer has set the boundary.</p><h3>ICR did not drop. It got smarter.</h3><p>The <a href="https://blogs.kameshs.dev/intent-compression-ratio-measuring-the-power-of-intent-ceb6faf2e2f9">Intent Compression Ratio</a> of this scaffold workflow is 48.</p><p>One conversation instruction.</p><p>48 distinct state-changing operations.</p><p>That number did not change when I added the decision tree.</p><p>What changed is that each operation now carries a risk assessment.</p><blockquote>The agent that runs 48 operations conservatively is more useful than the agent that runs 48 operations blindly.</blockquote><p>The metric I track is not only ICR.</p><p>I also track the agent-raised PR merge rate:</p><blockquote>The percentage of agent-created PRs that engineers merge without modification.</blockquote><p>That number measures trust.</p><p>A team at 60%+ merge rate has accepted the agent as part of the workflow.</p><p>A team at 10% has a noisy bot.</p><p>Same ICR. Different values.</p><p>Trust takes time.</p><p>The conservative ceiling builds it.</p><h3>What I mean by Agentic DevOps with CoCo</h3><p><strong>Not</strong>: replace engineers with AI.</p><p><strong>Yes</strong>: give engineers a CI pipeline that knows its limits.</p><p>The value is not faster code generation by itself.</p><p>Faster generation without better control creates faster review queues and faster mistakes.</p><p>The value is compressing the loop.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/302/1*PBZ29akrRTJYPBMsUumBJQ.png" /><figcaption>Where intent meets infrastructure: Integrating autonomous agents into traditional CI/CD loops.</figcaption></figure><p>That compression matters because the agent is calibrated.</p><p>It does not open a PR for every needs-review issue.</p><p>It does not silently change complex code because the prompt sounded confident.</p><p>It does not confuse autonomy with permission.</p><p>Agentic DevOps means the agent can say:</p><blockquote>I am not sure enough to act on this one.</blockquote><p>That is not a limitation.</p><p>That is the design.</p><h3>Why this matters</h3><p>Most AI engineering conversations still treat autonomy as the finish line.</p><p>Can the agent write the code?</p><p>Can it open the PR?</p><p>Can it deploy the change?</p><p>Those are useful questions, but they are not the first questions.</p><p>The first question should be:</p><blockquote>What has to be true before the agent is allowed to act?</blockquote><p>That is where intent becomes operational.</p><p>That is where policy becomes part of the development loop.</p><p>That is where CoCo, CI, and IDD start to become more useful than a smarter autocomplete.</p><p>Agentic DevOps is not about giving agents unlimited power.</p><p>It is about giving them:</p><ul><li>explicit intent</li><li>bounded authority</li><li>visible decisions</li><li>human override</li></ul><p>That is the CI agent I want.</p><p>One that knows how to act.</p><p>And one that knows when not to.</p><p>By making agentic authority visible and configurable, we are not just automating tasks; we are codifying intent. This is the final piece of the<a href="https://blogs.kameshs.dev/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c"> Intent-Driven Development</a> puzzle: ensuring that the ‘evidence’ generated by our agents maps directly back to human-authorized intent.</p><h3>Implementation References</h3><p>The article describes the Agentic DevOps pattern rather than depending on a single repository. The following public references provide implementation context for the GitHub, GitLab, and Snowflake CoCo workflows discussed in this post.</p><p><strong>CoCo skill</strong><br><a href="https://github.com/Snowflake-Labs/devops-snowflake-coco-agents">Snowflake-Labs/devops-snowflake-coco-agents</a><br>CoCo skill implementation for the DevOps agent workflow. This provides the core skill context behind scaffold, scan, issue routing, fix behavior, and the human-review loop.</p><p><strong>GitHub template</strong><br><a href="https://github.com/Snowflake-Labs/github-coco-agent">Snowflake-Labs/github-coco-agent</a><br>Template for exploring CoCo-driven Agentic DevOps workflows with GitHub Actions. It shows how the pattern maps to GitHub-native CI/CD, repository configuration, issue routing, and PR-based governance.</p><p><strong>GitLab template</strong><br><a href="https://github.com/snowflake-dev/gitlab-coco-agent">snowflake-dev/gitlab-coco-agent</a><br>Template for exploring the same Agentic DevOps workflow in GitLab CI. It shows that the pattern is not tied only to GitHub Actions and can be adapted across CI/CD systems.</p><h3>Further Reading</h3><p>This post continues my Intent-Driven Development series:</p><ul><li><a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14">Infrastructure-as-Intent: The Field Velocity Blueprint</a></li><li><a href="https://blogs.kameshs.dev/the-ghost-in-the-machine-why-ai-needs-the-spirit-of-uml-0d8864e583e2">The Ghost in the Machine: Why AI Needs the Spirit of UML</a></li><li><a href="https://blogs.kameshs.dev/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c">Intent-Driven Development: The Shift Developers Can’t Ignore</a></li><li><a href="https://blogs.kameshs.dev/intent-compression-ratio-measuring-the-power-of-intent-ceb6faf2e2f9">Intent Compression Ratio: Measuring the Power of Intent</a></li><li><a href="https://blogs.kameshs.dev/icr-and-token-economics-9a014a75b399">ICR and Token Economics</a></li><li><a href="https://blogs.kameshs.dev/from-intent-to-evidence-why-ai-skills-need-tests-a48b3a43d971">From Intent to Evidence: Why AI Skills Need Tests</a></li></ul><p>For related thinking, Andrew Ng’s recent writing on <a href="https://www.andrewng.org/writing/">Three Key Loops for Building Great Software</a> and his <a href="https://www.deeplearning.ai/courses/agentic-ai/">Agentic AI course</a> reinforce the same direction: reliable AI systems are built around iterative loops, not one-shot prompts.</p><p>That is the bridge this article explores.</p><p>Intent gives the loop purpose.<br>Evidence gives the loop trust.<br>Policy gives the loop boundaries.<br>Human review gives the loop judgment.</p><p><em>Disclosure: Feature image generated with AI and reviewed by the author.</em></p><h3>About the Author</h3><p><strong><em>Kamesh Sampath</em></strong><em> is a Lead Developer Advocate at Snowflake, author, and long-time open-source contributor with 25+ years in enterprise software. He works across data engineering and AI with developer communities, helping practitioners turn modern data platforms into systems that hold up in production.</em></p><p><em>Through talks, writing, and hands-on demos, Kamesh makes cloud, data, and AI topics easier to understand and apply — grounded in real-world constraints. His sessions mix deep technical detail with practical patterns that developers and data teams can apply right away.</em></p><p><em>Lately, he’s been speaking about Apache NiFi (Snowflake Openflow), AI (Snowflake Cortex), and PostgreSQL.</em></p><p><em>He believes technology becomes powerful when it is shared, taught, and built together.</em></p><p><a href="https://github.com/kameshsampath">GitHub</a> |<a href="https://linkedin.com/in/kameshsampath"> LinkedIn</a> |<a href="https://blogs.kameshs.dev/"> Blog</a> |<a href="https://x.com/kamesh_sampath"> X</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4bc8f38d61ba" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Intent to Evidence: Why AI Skills Need Tests]]></title>
            <link>https://kameshsampath.medium.com/from-intent-to-evidence-why-ai-skills-need-tests-a48b3a43d971?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/a48b3a43d971</guid>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[developer-tools]]></category>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Mon, 22 Jun 2026 13:37:42 GMT</pubDate>
            <atom:updated>2026-06-22T13:37:42.545Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Moving beyond “it worked once” to build verifiable, trustworthy AI agents in real-world developer workflows.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U1ek78t3bkD_91_5QrkH-A.png" /><figcaption>Intent is the source. Evidence is the contract.</figcaption></figure><p>AI skills should not be trusted because they worked once. They should be trusted because they can reach the intended state repeatedly, with executable tests proving the outcome.</p><p><strong>AI</strong> agents are easy to celebrate when they work once, but that is also the trap. The demo runs, the code looks right, the file appears in the right place, and everyone nods. Then you run it again. This time a file is missing, a parameter is read differently, or a command is skipped. The final result is close, but not correct. This is where AI-native development gets real: we do not only need agents that can generate; we need skills that can be trusted.</p><p>In my earlier posts on Infrastructure-as-Intent, Intent-Driven Development, Intent Compression Ratio, and token economics, I argued that developers are moving away from writing every step by hand and toward expressing outcomes. Not every command, not every click, and not every line of glue code — just the outcome. But there is a missing layer in that story. Intent needs evidence.</p><blockquote>Without evidence, intent is only a better prompt</blockquote><h3>“It Worked Once” Is Not Engineering</h3><p>Every developer knows this. We do not trust code because it compiled once, we do not trust infrastructure because one deployment succeeded, and we do not trust an API because one request returned 200 OK. We trust systems when we can verify them again and again.</p><p>AI skills should be held to the same standard. Today, many AI workflows still feel like demo-driven development: we run an agent, inspect the output, and if it looks good, we move on. That is fine for exploration, but it is not enough for reusable skills.</p><p>A reusable skill needs a stronger question. Not: <em>did the agent produce something useful?</em> But rather: <em>did the system reach the intended state, and can it do that again?</em></p><h3>The Gap Between Output and Outcome</h3><p>There is a gap between a plausible result and a proven result. I call this the <strong>Verification Gap</strong>. On one hand, the agent gives an answer that looks right. On the other hand, the system is in the correct state. That gap matters because AI agents operate through probabilistic reasoning. The same instruction can lead to different paths, tools, or decisions depending on context and model behavior. Often those paths converge on the same outcome. Sometimes they do not.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aJS3h7OAcbFdEwc9S88BAg.png" /><figcaption><em>The Verification Gap: The difference between an agent generating a plausible output and proving the system actually reached the correct state.</em></figcaption></figure><p>The mistake is not using AI; the mistake is assuming one successful run is proof. A better question is: how do we make non-deterministic behavior measurable enough for developers to work with? An answer that looks right is not the same as a system that is right.</p><h3>CoCo as the Proving Ground</h3><p>I encountered this problem firsthand while building skills for <a href="https://docs.snowflake.com/en/user-guide/cortex-code/cortex-code">Snowflake Cortex Code</a>, also known as CoCo. Because CoCo is an agentic coding assistant that combines reasoning, tool use, and code execution, it acts as a practical environment for testing AI skills in real-world workflows. I needed a way to prove that an agent’s answer wasn’t just plausible, but that the system was actually in the right state. That practical need led me to build inspect-coco.</p><p>A CoCo skill can package developer intent into a reusable capability. Instead of asking an agent to reason from scratch every time, we can give it a skill that captures a workflow, convention, tool pattern, or common engineering task. This is the shift I described in Infrastructure-as-Intent: we move from prescribing steps to expressing outcomes, turning a skill into a reusable unit of intent.</p><p>That is powerful, but it is also risky if we do not test it. A skill can hide many actions behind one instruction — it may create files, change configurations, call tools, or update a project. The user sees a simple request, and the skill performs the work. This is exactly what I mean by Intent Compression Ratio. A high-value skill compresses many steps into one intent, but high compression requires high confidence. If one intent represents ten steps, we need to know whether all ten steps completed correctly.</p><p>The same challenge exists whether you are building skills for CoCo, Claude Code, Codex, Gemini CLI, OpenHands, or any other agentic development environment. Once an agent can inspect repositories, modify files, execute commands, and automate parts of the software lifecycle, the question becomes the same: how do we know it did the right thing?</p><p>CoCo exposed the gap early. It became the first proving ground because it offered a practical environment where skills could interact with real projects, tools, and workflows. That made it easier to test a simple question: can intent be translated into repeatable, verifiable outcomes?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8W8QFXUSwUJQDXiIln-72w.png" /><figcaption><em>inspect-coco bridges the foundational evaluation capabilities of Inspect AI with the concrete execution environment of Snowflake CoCo.</em></figcaption></figure><p>While the pattern is not limited to CoCo, CoCo is the concrete runtime. Inspect AI is the evaluation foundation. inspect-coco connects the two.</p><h3>What inspect-coco Does ?</h3><p>inspect-coco is a developer-first test harness for the instructions and workflows that become AI skills. The flow is intentionally familiar:</p><ul><li>It uses <strong>Inspect AI</strong> to run evaluations.</li><li>It uses <strong>CoCo</strong> to execute the skill.</li><li>It uses <strong>Docker</strong> to isolate the environment.</li><li>It uses a <strong>test script</strong> to verify the result.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mzWOhWxuLiUUaJKduuCnFQ.png" /><figcaption><em>The core evaluation loop: translating intent into execution, verification, and reliable measurement.</em></figcaption></figure><p>The key is the test. inspect-coco does not ask another model whether the answer looks good; it runs the skill and checks the system state. Did the file exist? Did the content match? Did the command succeed, and did the project end up in the expected shape?</p><p>If yes, the test passes; if not, it fails. That is a language developers already understand. If you prefer, the verification layer can also use familiar testing frameworks such as pytest, allowing teams to reuse existing assertions, fixtures, and testing practices instead of learning a new evaluation model. The important idea is not the framework itself, but that the outcome is verified by executable tests rather than judged by appearance.</p><h3>Three Files</h3><p>An evaluation can start with just three files. That is intentional; no heavy platform is required to understand the idea.</p><ul><li>instruction.md (Markdown) describes the intent.</li><li>tests/test.sh (Shell script) verifies the outcome.</li><li>task.toml (TOML) tells the evaluation how to run.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yGzI_ovvSe0uBR2u1oDM9A.png" /><figcaption><em>A developer-first evaluation requires minimal overhead: intent, verification, and configuration.</em></figcaption></figure><p>The simplest example is the hello-world evaluation in the repository. The instruction asks the skill to create /workspace/hello.txt. The agent can choose its path, but the test objectively checks the result: does the file exist, and does the content match exactly? Exit 0 means pass; anything else means failure.</p><p>That is boring in the best possible way.</p><h3>Unit Tests for Skill Instructions</h3><p>One design choice in inspect-coco is worth calling out: it does not require starting with a fully packaged skill and running the whole lifecycle end to end. It can start with an instruction. That instruction may later become part of a CoCo skill, a Claude Code command, a Codex workflow, or another agentic environment. But before it becomes reusable automation, the instruction itself should be tested.</p><p>A skill is not only code; it is packaged intent. If the intent is vague, the skill will be vague. If the instruction is hard to verify, the skill will be hard to trust. If the constraints are weak, the agent has too much room to improvise. Because of this, inspect-coco treats the instruction as a testable artifact, making it closer to unit testing for instructions than demo testing for agents.</p><p>The question is not just whether the complete agent workflow can succeed. The earlier question is whether the instruction is clear, constrained, and verifiable enough to become a reliable skill in the first place. Instruction-level tests tell us if the unit of intent is well-formed before packaging it, while end-to-end tests tell us if the whole workflow actually worked. Both are useful, but they answer fundamentally different questions.</p><h3>Intent Needs Structure</h3><p>The instruction follows a simple structure, where each section has a specific job:</p><ul><li><strong>Goal:</strong> Says what we want.</li><li><strong>Requirements:</strong> Say what must be true.</li><li><strong>Constraints:</strong> Reduce unwanted choices.</li><li><strong>Output:</strong> Tells us what success looks like.</li></ul><p>This structure matters because vague intent creates drift. If the instruction is loose, the agent has more room to guess. It may still produce something useful, but useful is not the same as correct. A good instruction narrows the space. It does not remove all non-determinism, but it makes the outcome easier to test.</p><p>To enforce this, inspect-coco also includes an IDD-style check for instruction quality, catching weak instructions early. The framework does not only ask if the skill worked; it also asks if the intent was clear enough to test.</p><h3>Repeated Runs Matter</h3><p>One passing run is useful; it tells us the skill can work. But it does not tell us whether the skill is reliable. Because AI systems vary between runs, inspect-coco can execute the same task multiple times, measuring results across epochs and reporting pass rates such as pass@k.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JyFi0Wp_cy6kDXf3LHT9cg.png" /><figcaption><em>Measuring reliability: Because AI systems are non-deterministic, true confidence comes from repeated execution and measuring stability over time.</em></figcaption></figure><p>A skill that passes once out of three is not the same as a skill that passes ten out of ten. Both may look good in a demo, but only one is ready to trust. The goal is not to pretend AI is deterministic; rather, it is to measure how stable the outcome is. This is where evaluation becomes more than a checklist — it becomes a feedback loop.</p><h3>Why Not Just Use LLM-as-Judge?</h3><p>LLM-as-judge has value when the output is subjective. A summary may need to be judged for clarity, a response checked for tone, or a support answer reviewed for usefulness.</p><p>But many developer tasks are not subjective. A file exists or it does not. A test passes or it does not. A command exits cleanly or it does not. A generated project runs or it does not. For those cases, we should not ask for an opinion; we should run a test.</p><p>That is the design choice behind inspect-coco: when the outcome can be verified by the system, the system should verify it.</p><h3>Why Inspect AI ?</h3><p>I built inspect-coco on <a href="https://inspect.aisi.org.uk/">Inspect AI</a> because agent evaluation needs more than prompt scoring. Developer skills often act in a real environment: they create files, modify projects, run commands, and use tools. So, the evaluation framework needs to support real execution.</p><p>Inspect AI provides that foundation, giving us tasks, solvers, scorers, sandboxes, repeated runs, and logs. inspect-coco adds a CoCo-specific layer on top:</p><ul><li>CoCo execution</li><li>CoCo skill scaffolding</li><li>IDD instruction checks</li><li>Docker-based environments</li><li>Snowflake authentication via OAuth, personal access tokens, and JWT</li><li>Script-based verification</li></ul><p>That is the important split. Inspect AI is the general evaluation foundation, CoCo is the concrete runtime where I am proving the pattern, and inspect-coco connects the two for developers who want to build reliable CoCo skills today.</p><h3>Developer-First by Design</h3><p>I wanted this to feel like normal development. Not like a research project, not like a dashboard-first platform, and not like another abstract AI evaluation layer. A developer should be able to look at the files and understand what is happening:</p><ul><li><strong>Markdown</strong> for intent</li><li><strong>Shell</strong> for verification</li><li><strong>TOML</strong> for configuration</li><li><strong>Docker</strong> for isolation</li><li><strong>CLI</strong> for execution</li><li><strong>Pass or fail</strong> for confidence</li></ul><p>That is the whole point. AI changes how we build, but it should not remove the habits that made software engineering work.</p><h3>Evaluation Should Be Close to the Skill</h3><p>inspect-coco can scaffold evaluations from an existing CoCo plugin, meaning a skill can grow a test right beside it. This is important because testing should not feel like a separate ceremony. If writing evaluations is too far away from writing skills, developers will skip it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mzWOhWxuLiUUaJKduuCnFQ.png" /><figcaption><em>The local evaluation loop: Bringing testing closer to the skill development process prevents evaluation from becoming a skipped ceremony.</em></figcaption></figure><p>The closer the test is to the skill, the more likely it becomes part of the workflow. That is how software teams learned to treat tests, and AI skill development should learn the same lesson.</p><h3>From Prompt Engineering to Intent Engineering</h3><p>Prompt engineering taught us how to talk to models. Intent engineering asks a harder question: can we express the outcome clearly enough for a system to act on it? But there is an even more important question after that: can we prove the system reached that outcome?</p><p>This is where AI-native development has to mature. The future will not belong to teams that generate the most code; it will belong to teams that can express intent clearly, compress complexity safely, and verify outcomes continuously. CoCo skills give us a way to package intent, and inspect-coco gives us a way to test it. That is the bridge from intent to evidence.</p><p>In Intent-Driven Development, the question is not only whether the agent produced something. The better question is whether the system reached the intended state, and whether it can do it again. That is what we should measure, and that is what we should improve. That is what turns AI skills from impressive demos into reliable developer tools.</p><p>Intent is the source. Evidence is the contract. Every serious AI skill needs tests.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K4inKdZnD_u_O67feIyaGg.gif" /><figcaption>Demo GIF in the article showing inspect-coco in action.</figcaption></figure><h3>References and Further Reading</h3><p><strong>Project</strong></p><ul><li><a href="https://github.com/kameshsampath/inspect-coco/">GitHub repository</a></li><li><a href="https://kameshsampath.github.io/inspect-coco/">Documentation</a></li><li><a href="https://github.com/kameshsampath/inspect-coco/tree/main/examples/hello-world">hello-worldExample</a></li></ul><p><strong>Snowflake and CoCo</strong></p><ul><li><a href="https://www.google.com/search?q=https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-code">Snowflake Cortex Code documentation</a></li><li><a href="https://www.snowflake.com/en/developers/guides/sfguide-build-end-to-end-ai-app-on-snowflake/">Snowflake CoCo Quickstart</a></li></ul><p><strong>My IDD Series</strong></p><ul><li><a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14">Infrastructure-as-Intent: The Field Velocity Blueprint</a></li><li><a href="https://blogs.kameshs.dev/the-ghost-in-the-machine-why-ai-needs-the-spirit-of-uml-0d8864e583e2">The Ghost in the Machine: Why AI Needs the Spirit of UML</a></li><li><a href="https://blogs.kameshs.dev/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c">Intent-Driven Development: The Shift Developers Can’t Ignore</a></li><li><a href="https://blogs.kameshs.dev/intent-compression-ratio-measuring-the-power-of-intent-ceb6faf2e2f9">Intent Compression Ratio: Measuring the Power of Intent</a></li><li><a href="https://blogs.kameshs.dev/icr-and-token-economics-9a014a75b399">ICR and Token Economics</a></li></ul><p><strong>Related Reading</strong></p><ul><li><a href="https://inspect.ai-safety-institute.org.uk/">Inspect AI</a></li><li><a href="https://github.com/openai/evals">OpenAI Evals</a></li><li><a href="https://www.promptfoo.dev/">Promptfoo</a></li><li><a href="https://github.com/confident-ai/deepeval">DeepEval</a></li><li><a href="https://www.swebench.com/">SWE-bench</a></li></ul><h3>About the Author</h3><p><strong><em>Kamesh Sampath</em></strong><em> is a Lead Developer Advocate at Snowflake, author, and long-time open-source contributor with 25+ years in enterprise software. He works across data engineering and AI with developer communities, helping practitioners turn modern data platforms into systems that hold up in production.</em></p><p><em>Through talks, writing, and hands-on demos, Kamesh makes cloud, data, and AI topics easier to understand and apply — grounded in real-world constraints. His sessions mix deep technical detail with practical patterns that developers and data teams can apply right away.</em></p><p><em>Lately, he’s been speaking about Apache NiFi (Snowflake Openflow), AI (Snowflake Cortex), and PostgreSQL.</em></p><p><em>He believes technology becomes powerful when it is shared, taught, and built together.</em></p><p><a href="https://github.com/kameshsampath">GitHub</a> |<a href="https://linkedin.com/in/kameshsampath"> LinkedIn</a> |<a href="https://blogs.kameshs.dev/"> Blog</a> |<a href="https://x.com/kamesh_sampath"> X</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a48b3a43d971" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Who Is Actually Running This? Snowflake’s Executor Role, Explained]]></title>
            <link>https://kameshsampath.medium.com/who-is-actually-running-this-snowflakes-executor-role-explained-9520d24a8fa4?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/9520d24a8fa4</guid>
            <category><![CDATA[snowflake]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[database-architecture]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Wed, 10 Jun 2026 15:01:02 GMT</pubDate>
            <atom:updated>2026-06-10T15:01:02.591Z</atom:updated>
            <content:encoded><![CDATA[<p>A practical guide to executor roles, INVOKER_ROLE(), and avoiding execution-context bugs in Snowflake.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tqVD6ZCmCQGnWOFY2ZW8Wg.png" /><figcaption>The role that calls an object isn’t always the role that runs it.</figcaption></figure><p>You think you know which role is running your query. You probably don’t.</p><p>Your stored procedure failed.</p><p>You checked the role.</p><p>The role has the privilege.</p><p>It still failed.</p><p>Your task will not resume.</p><p>You own it.</p><p>You still can’t run it.</p><p>The bug is not in your SQL. It is in your mental model.</p><blockquote>In Snowflake, the role that <em>calls</em> an object is not the role that <em>runs</em> it.</blockquote><p>That role is the <em>executor role</em>. It explains a whole class of errors that look like permission bugs but are really execution-context bugs.</p><h3>The Mental Model</h3><p>When you run a SELECT, the role doing the work is your session role. Simple.</p><p>When you CALL a stored procedure, it depends. The role might be the procedure owner. It might be you. It might be something else entirely.</p><p>That “role doing the work” is the executor role.</p><p>Snowflake exposes two functions to see it:</p><ul><li>CURRENT_ROLE(): your session role, right now.</li><li>INVOKER_ROLE(): the role Snowflake is using to evaluate the currently executing object.</li></ul><p>At the session level, they return the same value. Inside a stored procedure, view, or task, they can diverge completely. That divergence is intentional. It is the whole point.</p><p>Most objects default to the owner. Most surprises come from not knowing what “owner” means in each context.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CF1b3KlpuN7R69OoKSqP7g.png" /><figcaption>Flowchart categorizing Snowflake objects by whether their execution context is configurable or not configurable.</figcaption></figure><h3>Follow Along</h3><p>New to Snowflake? <a href="https://signup.snowflake.com/">Start a free trial</a>. Your account is ready in minutes. Everything below uses SNOWFLAKE_SAMPLE_DATA, available in every account.</p><p><strong>Option A: Guided with Cortex Code</strong> <a href="https://signup.snowflake.com/?utm_source=cortexcode&amp;utm_medium=devrel">Sign up with Cortex Code for Developers</a>, clone the <a href="https://github.com/kameshsampath/executor-role-blog-demo">companion repo</a>, and run $executor-role-workshop. The skill walks through each concept, shows what will fail before it runs, and cleans up after itself.</p><p><strong>Option B: DIY with Cortex Code</strong> Open the companion repo in Cortex Code and follow this post section by section. The repo has the full runnable SQL for each example. This post is the guide.</p><h3>Decision Matrix</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q57pceOnt07GBERBRHtAtg.png" /><figcaption>Decision tree flowchart showing how to choose the right execution mode for Snowflake stored procedures, tasks, dynamic tables, and masking policies.</figcaption></figure><h3>The Privilege Illusion: Stored Procedures</h3><p>This is the executor role pattern I reach for most in the field.</p><h4>Owner’s Rights: The Firewall</h4><blockquote><strong>Rule:</strong> A stored procedure with EXECUTE AS OWNER runs with the procedure owner’s role. The caller’s session role has no effect on what SQL inside the procedure can access. The SQL below proves it.</blockquote><ul><li><strong>Roles:</strong> ACCOUNTADMIN to set up. data_engineer_role to create the procedure. analyst_role to call it. The analyst has no table access at all.</li></ul><p>You have a data_engineer_role that owns sensitive order data. You have an analyst_role that needs a specific, controlled view of that data. Direct table access is too broad.</p><p>Owner’s rights procedures solve exactly this. The procedure runs with the owner’s grants, not the caller’s. The analyst calls it. The data engineer role’s grants do the work. The analyst never touches the table directly.</p><pre>-- Full setup + teardown:<br>-- https://github.com/kameshsampath/executor-role-blog-demo/blob/main/sql/01-owners-rights-proc.sql<br><br>-- Create the proc as data_engineer_role<br>USE ROLE data_engineer_role;<br>USE WAREHOUSE COMPUTE_WH;<br><br>CREATE OR REPLACE PROCEDURE executor_demo.tpch.high_value_orders(min_price FLOAT)<br>  RETURNS TABLE (order_key NUMBER, total_price FLOAT, status VARCHAR)<br>  LANGUAGE SQL<br>  EXECUTE AS OWNER<br>AS<br>BEGIN<br>  LET rs RESULTSET := (<br>    SELECT o_orderkey::NUMBER, o_totalprice::FLOAT, o_orderstatus::VARCHAR<br>    FROM SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS<br>    WHERE o_totalprice &gt; :min_price<br>    ORDER BY o_totalprice DESC LIMIT 10<br>  );<br>  RETURN TABLE(rs);<br>END;<br><br>GRANT USAGE ON PROCEDURE executor_demo.tpch.high_value_orders(FLOAT) TO ROLE analyst_role;<br><br>-- Analyst calls the proc: data returned despite having no table access<br>USE ROLE analyst_role;<br>CALL executor_demo.tpch.high_value_orders(500000);<br>-- Expected: 10 rows of high-value orders<br><br>-- Analyst queries the table directly: blocked<br>SELECT o_orderkey, o_totalprice<br>FROM SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS LIMIT 5;<br>-- Expected: SQL access control error -- privilege [SELECT] not granted</pre><p>Owner’s rights mode is not a workaround. It is the access model.</p><h4>Caller’s Rights: The Window</h4><blockquote><strong>Rule:</strong> A stored procedure with EXECUTE AS CALLER runs in the caller’s session context. It can read session variables the caller set. An owner’s rights procedure cannot. The SQL below proves it.</blockquote><ul><li><strong>Roles:</strong> analyst_role owns and calls this procedure. No privilege delegation needed.</li></ul><p>Owner’s rights mode has one hard constraint: it cannot see your session.</p><p>No caller session variables.</p><p>No caller database context.</p><p>If your procedure needs to know who is calling it, or needs a value the caller set in the session, you need caller’s rights.</p><pre>-- Full setup + teardown:<br>-- https://github.com/kameshsampath/executor-role-blog-demo/blob/main/sql/01-owners-rights-proc.sql<br><br>USE ROLE analyst_role;<br>-- Set the variable BEFORE CREATE PROCEDURE: it is read at compile time<br>SET SEGMENT_FILTER = &#39;BUILDING&#39;;<br><br>CREATE OR REPLACE PROCEDURE executor_demo.tpch.customers_by_segment()<br>  RETURNS TABLE (cust_key NUMBER, name VARCHAR, segment VARCHAR, nation VARCHAR)<br>  LANGUAGE SQL<br>  EXECUTE AS CALLER<br>AS<br>DECLARE<br>  seg VARCHAR DEFAULT $SEGMENT_FILTER;<br>BEGIN<br>  RETURN TABLE(<br>    SELECT c_custkey, c_name, c_mktsegment, c_nationkey::VARCHAR<br>    FROM SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.CUSTOMER<br>    WHERE c_mktsegment = :seg LIMIT 10<br>  );<br>END;<br><br>-- Caller sets the variable, proc reads it<br>SET SEGMENT_FILTER = &#39;BUILDING&#39;;<br>CALL executor_demo.tpch.customers_by_segment();<br>-- Expected: 10 rows where c_mktsegment = &#39;BUILDING&#39;<br><br>SET SEGMENT_FILTER = &#39;AUTOMOBILE&#39;;<br>CALL executor_demo.tpch.customers_by_segment();<br>-- Expected: 10 rows where c_mktsegment = &#39;AUTOMOBILE&#39;<br></pre><p>Owner’s rights mode buys you privilege isolation. Caller’s rights mode buys you session context. You cannot have both in a single procedure.</p><p>The choice between owner’s rights and caller’s rights is the choice between what the <em>proc</em> can see and what the <em>caller</em> can see.</p><p><strong><em>(Note: </em></strong><em>There is one more mode</em><strong><em>EXECUTE AS RESTRICTED CALLER (Native Apps only).</em></strong><em> When a Native App provider needs access to consumer account objects, the consumer authorizes it explicitly: </em><em>GRANT CALLER USAGE ON DATABASE consumer_db TO APPLICATION my_app;. For standard procedures, the choice is OWNER or CALLER.)</em></p><h3>The Silent Task: EXECUTE TASK</h3><p>This one trips up everyone at least once. Including me.</p><blockquote><strong>Rule:</strong> OWNERSHIP on a task lets you change its state (ALTER TASK RESUME/SUSPEND). EXECUTE TASK ON ACCOUNT lets the scheduler actually fire the task body. These are two different privileges. You need both. The SQL below proves it.</blockquote><ul><li><strong>Roles:</strong> ACCOUNTADMIN to grant EXECUTE TASK ON ACCOUNT. pipeline_role to own and resume the task.</li></ul><p>You create a task.</p><p>You own it.</p><p>You resume it.</p><p>Snowflake says <strong>NO</strong>.</p><p>Cannot execute task, EXECUTE TASK privilege must be granted to owner role.</p><p>OWNERSHIP is not enough. EXECUTE TASK is account-level, separate from any object grant. You must grant it explicitly. Always.</p><p>There is also a subtler version. You grant EXECUTE TASK ON ACCOUNT. The task resumes. The state shows started. The task body never executes. OWNERSHIP/OPERATE handles state changes. EXECUTE TASK ON ACCOUNT is what lets the scheduler fire the body.</p><p>One task.</p><p>Two privileges.</p><pre>-- Full setup + teardown:<br>-- https://github.com/kameshsampath/executor-role-blog-demo/blob/main/sql/02-execute-task.sql<br><br>USE ROLE pipeline_role;<br>CREATE OR REPLACE TASK executor_demo.tpch.daily_order_stats<br>  WAREHOUSE = COMPUTE_WH<br>  SCHEDULE = &#39;USING CRON 0 6 * * * UTC&#39;<br>AS<br>  INSERT INTO executor_demo.tpch.order_stats (total_orders, avg_price)<br>  SELECT COUNT(*), AVG(o_totalprice) FROM SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS;<br><br>-- Without EXECUTE TASK: fails<br>ALTER TASK executor_demo.tpch.daily_order_stats RESUME;<br>-- Expected: Cannot execute task, EXECUTE TASK privilege must be granted to owner role<br><br>-- Grant it, then resume<br>USE ROLE ACCOUNTADMIN;<br>GRANT EXECUTE TASK ON ACCOUNT TO ROLE pipeline_role;<br><br>USE ROLE pipeline_role;<br>ALTER TASK executor_demo.tpch.daily_order_stats RESUME;<br>-- Expected: Statement executed successfully</pre><p>Ownership means you control the object. EXECUTE TASK means you are allowed to fire it. Snowflake separates those deliberately.</p><h3>The Ghost User: Dynamic Tables</h3><p>The data looks correct. The numbers are wrong. The DT refreshed without error.</p><blockquote><strong>Rule:</strong> A dynamic table’s background refresh runs as an internal SYSTEM user by default. A masking policy that checks CURRENT_USER() will never see a named user during that refresh. The data is silently masked. EXECUTE AS USER pins the refresh to a named identity. The SQL below shows the broken case first, then the fix.</blockquote><ul><li><strong>Roles:</strong> ACCOUNTADMIN to create the service user and grant IMPERSONATE. transform_role to own the table and the dynamic table.</li></ul><p>Dynamic tables refresh in the background. The refresh runs as the owner role. That is fine for most transformations.</p><p>It becomes a problem when your masking or row access policies check CURRENT_USER(). By default, the refresh executor is an internal SYSTEM user. Your policy does not match it. The data comes back masked. The DT refreshes without error.</p><p>EXECUTE AS USER, lets you pin the refresh to a named service user.</p><pre>-- Full setup + teardown:<br>-- https://github.com/kameshsampath/executor-role-blog-demo/blob/main/sql/03-dynamic-table.sql<br><br>USE ROLE ACCOUNTADMIN;<br>CREATE USER IF NOT EXISTS svc_transform<br>  DEFAULT_ROLE = transform_role MUST_CHANGE_PASSWORD = FALSE;<br>GRANT ROLE transform_role TO USER svc_transform;<br>GRANT IMPERSONATE ON USER svc_transform TO ROLE transform_role;<br><br>USE ROLE transform_role;<br><br>-- Masking policy: show real values only when refresh runs as SVC_TRANSFORM<br>CREATE OR REPLACE MASKING POLICY executor_demo.tpch.mask_price AS<br>(val NUMBER) RETURNS NUMBER -&gt;<br>CASE WHEN CURRENT_USER() = &#39;SVC_TRANSFORM&#39; THEN val ELSE -1 END;<br><br>-- Base table with policy applied (see full SQL for setup)<br>-- executor_demo.tpch.lineitem_prices already has mask_price on l_extendedprice<br><br>-- STEP 1: Create DT WITHOUT EXECUTE AS USER (the broken case)<br>CREATE OR REPLACE DYNAMIC TABLE executor_demo.tpch.dt_revenue_broken<br>  TARGET_LAG = &#39;1 hour&#39;<br>  WAREHOUSE = COMPUTE_WH<br>AS<br>  SELECT l_orderkey,<br>         SUM(l_extendedprice * (1 - l_discount)) AS net_revenue<br>  FROM executor_demo.tpch.lineitem_prices GROUP BY l_orderkey;<br><br>ALTER DYNAMIC TABLE executor_demo.tpch.dt_revenue_broken REFRESH;<br>SELECT * FROM executor_demo.tpch.dt_revenue_broken LIMIT 5;<br>-- Expected: net_revenue = -1 for all rows<br>-- CURRENT_USER() during refresh = internal SYSTEM user, not SVC_TRANSFORM<br>-- Policy masks everything. Refresh succeeds. Data is silently wrong.<br><br>-- STEP 2: Create DT WITH EXECUTE AS USER (the fix)<br>CREATE OR REPLACE DYNAMIC TABLE executor_demo.tpch.dt_revenue<br>  TARGET_LAG = &#39;1 hour&#39;<br>  WAREHOUSE = COMPUTE_WH<br>  EXECUTE AS USER svc_transform<br>AS<br>  SELECT l_orderkey,<br>         SUM(l_extendedprice * (1 - l_discount)) AS net_revenue<br>  FROM executor_demo.tpch.lineitem_prices GROUP BY l_orderkey;<br><br>ALTER DYNAMIC TABLE executor_demo.tpch.dt_revenue REFRESH;<br>SELECT * FROM executor_demo.tpch.dt_revenue LIMIT 5;<br>-- Expected: real net_revenue values<br>-- CURRENT_USER() during refresh = SVC_TRANSFORM. Policy unmasks. Data is correct.</pre><p>EXECUTE AS USER is not about security. It is about identity. Policies need to know who is running the refresh. You have to tell Snowflake.</p><h3>The View Trap: Masking Policies</h3><p>This is the one that quietly breaks the most carefully designed security models.</p><blockquote><strong>Rule:</strong> INVOKER_ROLE() inside a masking policy returns the role of the currently executing object’s owner, not the session role. Through a view, that is the view owner’s role, not the querying user’s role. The SQL below proves it.</blockquote><p><strong>Roles:</strong> ACCOUNTADMIN for policy creation and grants. view_creator_role to own the view and base table. analyst_role to query both.</p><p>You apply a masking policy using INVOKER_ROLE(). The rule says: if the invoker is ANALYST_ROLE, show the data. You run as ANALYST_ROLE. You query the table. The data is visible. Correct.</p><p>Then you query the same data through a view. Masked.</p><p>INVOKER_ROLE() does not return your session role here. When you query through a view, it returns the view owner’s role. Not yours.</p><pre>-- Full setup + teardown:<br>-- https://github.com/kameshsampath/executor-role-blog-demo/blob/main/sql/04-masking-invoker-role.sql<br>-- https://github.com/kameshsampath/executor-role-blog-demo/blob/main/sql/05-masking-caller-role.sql<br><br>-- Policy using INVOKER_ROLE<br>USE ROLE ACCOUNTADMIN;<br>CREATE OR REPLACE MASKING POLICY executor_demo.tpch.mask_order_price AS<br>(val NUMBER) RETURNS NUMBER -&gt;<br>CASE WHEN INVOKER_ROLE() = &#39;ANALYST_ROLE&#39; THEN val ELSE -1 END;<br><br>-- View owned by view_creator_role (not analyst_role)<br>USE ROLE view_creator_role;<br>CREATE OR REPLACE VIEW executor_demo.tpch.orders_view AS<br>SELECT * FROM executor_demo.tpch.orders_sample;<br><br>-- Analyst queries the table directly<br>USE ROLE analyst_role;<br>SELECT o_orderkey, o_totalprice FROM executor_demo.tpch.orders_sample LIMIT 5;<br>-- Expected: real prices  (INVOKER_ROLE = ANALYST_ROLE)<br><br>-- Analyst queries through the view<br>SELECT o_orderkey, o_totalprice FROM executor_demo.tpch.orders_view LIMIT 5;<br>-- Expected: -1  (INVOKER_ROLE = VIEW_CREATOR_ROLE, not ANALYST_ROLE)<br>-- Your session is ANALYST_ROLE. INVOKER_ROLE() does not care.<br><br>-- Fix: use CURRENT_ROLE() instead<br>USE ROLE ACCOUNTADMIN;<br>ALTER TABLE executor_demo.tpch.orders_sample<br>  MODIFY COLUMN o_totalprice UNSET MASKING POLICY;<br>CREATE OR REPLACE MASKING POLICY executor_demo.tpch.mask_order_price AS<br>(val NUMBER) RETURNS NUMBER -&gt;<br>CASE WHEN CURRENT_ROLE() = &#39;ANALYST_ROLE&#39; THEN val ELSE -1 END;<br>ALTER TABLE executor_demo.tpch.orders_sample<br>  MODIFY COLUMN o_totalprice<br>  SET MASKING POLICY executor_demo.tpch.mask_order_price;<br><br>USE ROLE analyst_role;<br>SELECT o_orderkey, o_totalprice FROM executor_demo.tpch.orders_view LIMIT 5;<br>-- Expected: real prices  (CURRENT_ROLE = ANALYST_ROLE regardless of view ownership)<br></pre><p>The policy was logically correct. The mental model was wrong.</p><p>Use INVOKER_ROLE() to enforce by object ownership. Use CURRENT_ROLE() to enforce by session identity. They are not interchangeable.</p><h3>When to Use What</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b54f89e98059a3e81db80fb0a62ecbd6/href">https://medium.com/media/b54f89e98059a3e81db80fb0a62ecbd6/href</a></iframe><h3>What To Do Next</h3><p>Run the examples. Or let the skill do it for you. $executor-role-workshop in Cortex Code walks through each concept with live SQL, shows what will fail before it runs, and cleans up after itself. <a href="https://github.com/kameshsampath/executor-role-blog-demo">Companion repo</a>.</p><p>When your next stored procedure fails with a permission error you cannot explain, check which EXECUTE AS mode it is running in. When your task refuses to resume, check for EXECUTE TASK at the account level. When your masking policy behaves differently through a view, check which context function you are using.</p><p>The executor role is not a feature. It is a model. Once you have the model, the errors explain themselves.</p><h3>Resources</h3><ul><li><a href="https://github.com/kameshsampath/executor-role-blog-demo">Companion repo + Cortex Code skill</a>: full runnable SQL + interactive workshop</li><li><a href="https://github.com/Snowflake-Labs/sf-cheatsheets/blob/main/executor-role-cheatsheet.md">Executor Role Developer Cheatsheet</a>: decision matrix, tips, troubleshooting table</li><li><a href="https://docs.snowflake.com/en/developer-guide/stored-procedure/stored-procedures-rights">Caller’s and Owner’s Rights Stored Procedures</a></li><li><a href="https://docs.snowflake.com/en/release-notes/2026/other/2026-02-18-dynamic-tables-execute-as-user">Dynamic Tables: EXECUTE AS USER</a>: February 2026 release</li><li><a href="https://docs.snowflake.com/en/sql-reference/functions/invoker_role">INVOKER_ROLE Function</a></li></ul><h3>About the Author</h3><p><strong><em>Kamesh Sampath</em></strong><em> is a Lead Developer Advocate at Snowflake, author, and long-time open-source contributor with 25+ years in enterprise software. He works across data engineering and AI with developer communities, helping practitioners turn modern data platforms into systems that hold up in production.</em></p><p><em>Through talks, writing, and hands-on demos, Kamesh makes cloud, data, and AI topics easier to understand and apply — grounded in real-world constraints. His sessions mix deep technical detail with practical patterns that developers and data teams can apply right away.</em></p><p><em>Lately, he’s been speaking about Apache NiFi (Snowflake Openflow), AI (Snowflake Cortex), and PostgreSQL.</em></p><p><em>He believes technology becomes powerful when it is shared, taught, and built together.</em></p><p><a href="https://github.com/kameshsampath">GitHub</a> |<a href="https://linkedin.com/in/kameshsampath"> LinkedIn</a> |<a href="https://blogs.kameshs.dev/"> Blog</a> |<a href="https://x.com/kamesh_sampath"> X</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9520d24a8fa4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ICR and Token Economics]]></title>
            <link>https://kameshsampath.medium.com/icr-and-token-economics-9a014a75b399?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/9a014a75b399</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[developer-experience]]></category>
            <category><![CDATA[platform-engineering]]></category>
            <category><![CDATA[agents]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Wed, 20 May 2026 03:26:00 GMT</pubDate>
            <atom:updated>2026-05-20T03:26:00.554Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Why the hidden cost of low-intent AI systems is not just more tokens, but weaker architecture.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qyELasZCRbrEjdJ6Op4Vdg.png" /><figcaption>From prompt optimization to intent optimization: higher ICR means more useful work, fewer wasted tokens, and better token economics.</figcaption></figure><h3>Introduction</h3><p>We talk a lot about tokens.</p><p>Input tokens.<br>Output tokens.<br>Context windows.<br>Pricing.<br>Latency.<br>Caching.</p><p>But I think we are missing the deeper question.</p><blockquote>Why are we using so many tokens in the first place?</blockquote><p>Most conversations about token optimization start too late. They focus on trimming prompts, compressing context, caching responses, or choosing cheaper models.</p><p>Those things matter.</p><p>But they are symptoms.</p><p>The deeper issue is often not the model.</p><p>It is the interaction architecture.</p><p>A weak interaction architecture forces the user to explain too much, repeat too much, clarify too much, and carry too much system knowledge in every request.</p><p>That is not just a usability problem.</p><p>It is an economic problem.</p><p>Because in AI-native systems, tokens are no longer just text.</p><p>Tokens are infrastructure.</p><p>They affect cost.<br>They affect latency.<br>They affect context capacity.<br>They affect reliability.<br>They affect how much useful work an AI system can perform before the interaction collapses under its own verbosity.</p><p>This is where Intent Compression Ratio (ICR) becomes useful.</p><p>In my previous article, <a href="https://blogs.kameshs.dev/intent-compression-ratio-measuring-the-power-of-intent-ceb6faf2e2f9">Intent Compression Ratio: Measuring the Power of Intent</a>, I looked at ICR as a way to understand how much procedural complexity moves from the developer to the system.</p><p>In this post, I want to look at the same idea from another angle:</p><blockquote>What happens to token economics when intent is poorly compressed?</blockquote><h3>The Token Problem Is Not Just Token Count</h3><p>It is tempting to think token optimization means making prompts shorter.</p><p>That is only partially true.</p><p>A short prompt can be bad if it is ambiguous.</p><p>A long prompt can be necessary if the system has no context.</p><p>The real question is simpler:</p><blockquote>How many tokens does the system need to faithfully understand and fulfill the user’s intent?</blockquote><p>That is the <strong>economic unit</strong> that matters.</p><p>Not prompt length alone.</p><p>Not output length alone.</p><p>But the total token footprint required to move from intent to outcome.</p><p>That includes:</p><ul><li>The first prompt.</li><li>The clarification turns.</li><li>The repeated context.</li><li>The hidden system instructions.</li><li>The tool call explanations.</li><li>The retries.</li><li>The corrections.</li><li>The familiar “no, that is not what I meant” loop.</li></ul><p>When you add all of that up, a small human request can become a very large machine interaction.</p><p>I call this <strong>token amplification</strong>.</p><h3>Token Amplification</h3><p>Token amplification happens when compact human intent expands into a disproportionately large token footprint.</p><p>For example, a developer may say:</p><blockquote>Deploy the payment service with autoscaling and observability.</blockquote><p>That is a compact intent.</p><p>But in a low-ICR system, the developer may need to explain:</p><ul><li>What service means.</li><li>Which environment.</li><li>Which runtime.</li><li>Which scaling rules.</li><li>Which observability stack.</li><li>Which naming conventions.</li><li>Which security constraints.</li><li>Which deployment policy.</li><li>Which rollback behavior.</li><li>Which team standards.</li></ul><p>Then the system asks clarifying questions.</p><p>Then the user repeats context.</p><p>Then the system generates something close, but not quite right.</p><p>Then the user corrects it.</p><p>By the end, the original intent has become a long token trail.</p><p>The system did not use more tokens because it understood more.</p><p>It used more tokens because it understood less.</p><p>That distinction matters.</p><h3>ICR as an Economic Signal</h3><p>For this discussion, I use a deliberately simple version of ICR:</p><blockquote><strong>ICR = Intent Fulfilled / Total Tokens</strong></blockquote><p>Higher ICR means more useful work completed per token consumed.</p><p>Lower ICR means the system burns more tokens to achieve the same outcome.</p><p>This makes ICR more than a developer experience idea.</p><p>It is also a cost metric.</p><p>A high-ICR system does not merely feel better to use.</p><p>It is more economical.</p><p>It reduces repeated explanations.<br>It reduces clarification loops.<br>It reduces redundant context transfer.<br>It reduces orchestration overhead.<br>It reduces the probability of retries.</p><p>That means token optimization is not only a prompt engineering problem.</p><blockquote>It is an intent design problem.</blockquote><h3>Prompt Verbosity Is Often a Tax on Missing Intent</h3><p>We have all seen this pattern.</p><p>A prompt starts simple.</p><p>Then it grows.</p><p>“Act as a senior engineer…”</p><p>“Follow these conventions…”</p><p>“Use this format…”</p><p>“Do not forget…”</p><p>“Before answering…”</p><p>“Assume the following context…”</p><p>“Here are the constraints…”</p><p>“Here are examples…”</p><p>“Here are exceptions to the examples…”</p><p>Some of this is useful.</p><p>But often, prompt verbosity is compensating for missing system memory, missing context, missing abstractions, or missing intent contracts.</p><p>The user is not just expressing the goal.</p><p>The user is rebuilding the world around the goal.</p><p>Every time.</p><p>That is expensive.</p><p>Not only in dollars, but in cognition.</p><p>The developer becomes responsible for carrying the system model in their head and serializing it into text repeatedly.</p><p>That is not intelligence.</p><p>That is manual context transport.</p><h3>Skills Are Where Intent Becomes Executable</h3><p>Intent by itself is not enough.</p><p>If I say:</p><blockquote>Deploy the payment service.</blockquote><p>The system still needs to know what “deploy” means in my environment.</p><p>Does it mean Kubernetes? <a href="https://docs.snowflake.com/en/developer-guide/snowpark-container-services/overview">Snowpark Container Services</a>? Terraform? A CI/CD pipeline? A staging rollout first? Which observability defaults? Which governance rules? Which rollback policy?</p><p>This is why skills are becoming such an important primitive in agentic systems.</p><p>Claude has Skills. Cortex Code has Skills. Gemini has Gems. Codex has Skills.</p><p>Different names, but they point in a similar direction.</p><p>A skill is not just a saved prompt.</p><p>At its best, a skill is compressed operational knowledge.</p><p>It can encode the steps, constraints, conventions, tools, expected output, safety boundaries, and domain-specific judgment required to complete a recurring task.</p><p>Without skills, the user has to serialize all of that context into the prompt.</p><p>With skills, the system can load the right operational pattern and execute the intent with less repeated explanation.</p><p>That is ICR in practice.</p><p>The human expression gets smaller, but the executable meaning gets richer.</p><h3>The Economics of Repetition</h3><p>The point is not to make every prompt shorter.</p><p>The point is to stop repeating what the system should already know.</p><p>A lot of token waste does not come from the user’s intent.</p><p>It comes from everything wrapped around the intent.</p><p>The same architectural context.<br>The same coding standards.<br>The same deployment rules.<br>The same governance constraints.<br>The same output format.<br>The same safety instructions.</p><p>Again and again.</p><p>In a single prompt, this feels harmless.</p><p>Across a workflow, it becomes expensive.</p><p>Across agents, tools, and retries, it becomes amplification.</p><p>This is the economics of repetition.</p><p>Low-ICR systems make humans restate context.</p><p>High-ICR systems turn repeated context into reusable capability.</p><p>That is why skills matter.</p><p>A skill is one way to stop paying the repetition tax every time. It packages recurring operational knowledge so the user can express the outcome instead of re-explaining the procedure.</p><p>The token savings are not just from fewer words.</p><p>They come from moving repeated meaning out of the prompt path and into the system.</p><p>But compression creates a second responsibility: transparency.</p><h3>A Small Experiment: ICR Lab</h3><p>To make this visible, I built a small open-source <a href="https://streamlit.io">Streamlit</a> app called <a href="https://github.com/kameshsampath/icr-lab">ICR Lab</a>.</p><p>The app is intentionally simple. It does not call a live LLM, benchmark models, or try to be an agent framework.</p><p>It simulates token behavior across different interaction styles so the economics are easier to see.</p><p>The current version compares five modes:</p><ul><li><strong>Verbose Prompting</strong>: large prompts with repeated context.</li><li><strong>Clarification Heavy</strong>: multiple rounds of back-and-forth before the system can act.</li><li><strong>Context-Aware</strong>: the system reuses some context and reduces repeated explanation.</li><li><strong>Intent-Optimized</strong>: the user expresses compact semantic intent and the system assumes more structured responsibility.</li><li><strong>Over-Compressed</strong>: the user strips so much context that the system misinterprets the intent, triggering correction rounds that amplify tokens beyond what a verbose prompt would have cost.</li></ul><p>The line I keep coming back to is this:</p><blockquote>Same intent. Different interaction architecture. Different token economics.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5SuRyh4lnI8Kfq7k7NpSJQ.png" /><figcaption>Same intent. Different interaction architecture. Different token economics. ICR Lab visualizes how token amplification changes across interaction modes.</figcaption></figure><p>That is the entire point of the demo.</p><p>The task does not change.</p><p>The model does not change.</p><p>The interaction pattern changes.</p><p>And the token footprint changes with it.</p><p>The fifth mode exists to prevent the wrong takeaway. Over-compression is not optimization. When the system lacks enough shared context to decompress a terse intent, it guesses wrong — and the correction cost exceeds the savings.</p><p>ICR is not “make prompts shorter”. It is “make meaning denser”.</p><h3>The Wrong Lesson: Just Make Prompts Shorter</h3><p>There is a dangerous interpretation here.</p><p>Someone might say:</p><blockquote>So high ICR means shorter prompts?</blockquote><p>Not exactly.</p><p>A short prompt with no shared context is just ambiguity.</p><p>A high-ICR interaction is not merely brief.</p><p>It is dense.</p><p>It carries more meaning per token because the surrounding system understands the domain, constraints, policies, tools, and desired state.</p><p>That understanding can come from many places:</p><ul><li>Platform profiles.</li><li>Reusable skills.</li><li>Semantic memory.</li><li>Declarative manifests.</li><li>Typed tool interfaces.</li><li>Repository context.</li><li>Organizational conventions.</li></ul><p>The point is not to delete words.</p><p>The point is to stop repeating what the system should already know.</p><h3>Compression Without Visibility Is Opacity</h3><p>There is another trap.</p><p>High compression can become dangerous when it hides too much.</p><p>If I say:</p><blockquote>Deploy the connector.</blockquote><p>And the system creates users, roles, policies, network rules, secrets, and tasks behind the scenes, I may get a great experience.</p><p>Until something breaks.</p><p>Then I need to know what was created, in what order, with what constraints, which step failed, what can be retried safely, and what state was left behind.</p><p>This is why ICR must be paired with transparency.</p><p>The system should absorb complexity, but not erase visibility.</p><p>High ICR without transparency is magic.</p><p>High ICR with transparency is engineering.</p><p>That distinction matters even more when money enters the picture.</p><p>Token-efficient systems that are impossible to debug only move the cost elsewhere.</p><p>They save tokens on the happy path and burn engineering hours during failure.</p><h3>From Prompt Optimization to Intent Optimization</h3><p>Prompt optimization asks:</p><blockquote>How do I phrase this better?</blockquote><p>Intent optimization asks:</p><blockquote>Why does the user need to phrase this much at all?</blockquote><p>That is the deeper shift.</p><p>The future of AI systems will not be defined only by larger context windows.</p><p>Larger context windows help, but they can also hide bad architecture.</p><p>If the system can accept more context, we may simply dump more context into it: more documents, more history, more instructions, more examples, more logs, more everything.</p><p>But stuffing the context window is not the same as understanding intent.</p><p>A bigger backpack does not make you organized.</p><p>It just lets you carry more mess.</p><p>The better direction is often not:</p><blockquote>Give the model more tokens.</blockquote><p>It is:</p><blockquote>Require fewer tokens to preserve the same intent.</blockquote><p>That is <strong>intent optimization</strong>.</p><h3>What ICR Starts to Reveal</h3><p>Once you look at AI systems through ICR, a few things become obvious.</p><p>A verbose prompt is not always rich intent.</p><p>Sometimes it is a workaround for missing abstraction.</p><p>A clarification loop is not always intelligence.</p><p>Sometimes it is the system externalizing its uncertainty back onto the user.</p><p>A large context window is not always better architecture.</p><p>Sometimes it is a bigger place to store repeated noise.</p><p>An agent workflow is not automatically efficient.</p><p>Sometimes agents multiply token usage by passing bloated context between themselves.</p><p>And a skill is not just automation.</p><p>It is a compressed unit of repeatable intent.</p><p>That last point matters.</p><p>In <a href="https://blogs.kameshs.dev/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c">Intent Driven Development</a>, I argued that developers are not becoming less important because AI can generate code.</p><p>They are becoming more important because the bottleneck has moved.</p><p>The bottleneck is no longer syntax.</p><p>It is clarity of intent.</p><p>Token economics reinforces that shift.</p><p>A vague developer with AI creates token sprawl.</p><p>A precise developer with intent-aware systems creates leverage.</p><p>After seeing these patterns, the next question is natural:</p><blockquote>Why simulate this instead of using a live LLM?</blockquote><h3>Why ICR Lab Uses Simulation</h3><p>ICR Lab v1 uses deterministic simulation instead of live model calls.</p><p>That is deliberate.</p><p>The goal is not to benchmark OpenAI, Anthropic, Snowflake Cortex, local models, or anything else.</p><blockquote>The goal is to isolate the interaction pattern.</blockquote><p>If the app used live LLM calls immediately, the conversation would drift into model quality.</p><p>Which model is cheaper?<br>Which model answers better?<br>Which model counts tokens differently?<br>Which model follows instructions more reliably?</p><p>Those are valid questions.</p><p>But they are not the question I wanted to explore.</p><p>The question is simpler:</p><blockquote>How does interaction architecture change token economics?</blockquote><p>Simulation keeps that visible.</p><p>Same input.<br>Same modes.<br>Same curves.<br>Same comparison.</p><p>No API keys.<br>No model variance.<br>No hidden backend.</p><p>Live model integrations can come later.</p><p>The concept needs to be clear before the instrumentation becomes real.</p><h3>Closing Thought</h3><p>The future may not belong to systems that consume the most tokens.</p><p>It may belong to systems that need the fewest tokens to understand us correctly.</p><p>That does not mean smaller models.</p><p>It does not mean shorter prompts.</p><p>It does not mean hiding complexity behind magical interfaces.</p><p>It means better intent architecture.</p><p>Systems that know the context.<br>Systems that preserve constraints.<br>Systems that compress repeated patterns.<br>Systems that expose what they did.<br>Systems that turn verbose human instruction into transparent machine execution.</p><p>That is where ICR and token economics meet.</p><p>Not in counting tokens for the sake of counting tokens.</p><p>But in asking a better question:</p><blockquote>How much useful intent did this system fulfill per token consumed?</blockquote><p>That question changes the optimization target.</p><p>And once you see it, token cost stops looking like a pricing problem alone.</p><blockquote>It starts looking like a design problem.</blockquote><h3>Try the Demo</h3><p>I built a small companion app for this post: <a href="https://icr-lab.streamlit.app/">ICR Lab</a>.</p><p>It is open source and runs as a Streamlit app.</p><p>The goal is simple:</p><p>Enter a task.<br>Compare interaction modes.<br>Watch token amplification happen.<br>Then watch it collapse as intent gets better compressed.</p><p>It is not a benchmark.</p><p>It is a playground for building intuition.</p><p>Because sometimes the best way to understand a systems problem is to make the invisible curve visible.</p><h3>Related Reading</h3><ul><li><a href="https://github.com/kameshsampath/icr-lab">ICR Lab GitHub Repo</a></li><li><a href="https://icr-lab.streamlit.app/">ICR Lab Live App</a></li><li><a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14">Infrastructure as Intent: The Field Velocity Blueprint</a></li><li><a href="https://blogs.kameshs.dev/the-ghost-in-the-machine-why-ai-needs-the-spirit-of-uml-0d8864e583e2">The Ghost in the Machine: Why AI Needs the Spirit of UML</a></li><li><a href="https://blogs.kameshs.dev/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c">Intent Driven Development: The Shift Developers Can’t Ignore</a></li><li><a href="https://blogs.kameshs.dev/intent-compression-ratio-measuring-the-power-of-intent-ceb6faf2e2f9">Intent Compression Ratio: Measuring the Power of Intent</a></li></ul><h3>About the Author</h3><p><strong><em>Kamesh Sampath</em></strong><em> is a Lead Developer Advocate at Snowflake, author, and long-time open-source contributor with 25+ years in enterprise software. He works across data engineering and AI with developer communities, helping practitioners turn modern data platforms into systems that hold up in production.</em></p><p><em>Through talks, writing, and hands-on demos, Kamesh makes cloud, data, and AI topics easier to understand and apply — grounded in real-world constraints. His sessions mix deep technical detail with practical patterns that developers and data teams can apply right away.</em></p><p><em>Lately, he’s been speaking about Apache NiFi (Snowflake Openflow), AI (Snowflake Cortex), and PostgreSQL.</em></p><p><em>He believes technology becomes powerful when it is shared, taught, and built together.</em></p><p><a href="https://github.com/kameshsampath">GitHub</a> |<a href="https://linkedin.com/in/kameshsampath"> LinkedIn</a> |<a href="https://blogs.kameshs.dev/"> Blog</a> |<a href="https://x.com/kamesh_sampath"> X</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9a014a75b399" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Intent Compression Ratio: Measuring the Power of Intent]]></title>
            <link>https://kameshsampath.medium.com/intent-compression-ratio-measuring-the-power-of-intent-ceb6faf2e2f9?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/ceb6faf2e2f9</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[developer-tools]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[snowflake]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Mon, 11 May 2026 15:40:46 GMT</pubDate>
            <atom:updated>2026-05-11T15:40:46.334Z</atom:updated>
            <content:encoded><![CDATA[<p><em>A way to measure how much procedural complexity moves from developers to AI agents, skills, and intent-driven systems.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FXcDXPEIew1QLQ07hQ0U3w.png" /><figcaption><em>From many operations to one intent: visualizing Intent Compression Ratio.</em></figcaption></figure><p>We’ve spent years optimizing execution.</p><p>Better APIs.<br>Better CLIs.<br>Better automation.</p><p>But developers don’t struggle because execution is slow.</p><p>They struggle because execution is hidden.</p><p>Infrastructure doesn’t fail loudly.<br>It fails silently — when you get the order or the desired state wrong.</p><h3>The Problem Isn’t Steps. It’s the Graph</h3><p>Take a realistic ask:</p><blockquote><em>“I need to deploy a connector with Snowflake </em><a href="https://docs.snowflake.com/en/user-guide/data-integration/openflow/about"><em>Openflow</em></a><em>.”</em></blockquote><p>In practice, this often ends with generating a <strong>Programmatic Access Token (PAT)</strong> to be used by clients like <a href="https://nipyapi.readthedocs.io/en/latest/"><strong>Apache NiPyAPI</strong></a>.</p><p>But getting there is not one step.</p><p>It’s a graph.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hvEqwTlmIYxSSY8oqiimcA.png" /><figcaption><em>Programmatic Access Token flow as an execution graph: multiple object operations compressed into one intent.</em></figcaption></figure><p>Each node is valid on its own.</p><p>But the ordering and desired state are not optional.</p><p>A network policy must exist before it’s attached.<br>An auth policy must reference the correct role.<br>The PAT must be generated after all constraints are satisfied.</p><p>Get this wrong?</p><p>No exception.<br>No obvious failure.</p><p>Just a token that doesn’t authenticate.<br>A connector that doesn’t connect.<br>And hours spent debugging across identity, network, and auth layers.</p><p>Infrastructure is <strong>not procedural</strong>.</p><p>It is <strong>relational</strong>.</p><p>And we’ve been asking developers to execute that relationship manually.</p><p>This is also why many AI agent workflows struggle today. We ask agents to operate on systems that still expose too much procedural complexity.</p><p>If an agent has to reason through 12 procedural steps to achieve one outcome, the probability of a hallucinated configuration, missed dependency, or ordering error rises quickly.</p><p>This is where <strong>Intent Compression Ratio(ICR)</strong> starts to matter.</p><p>High ICR becomes a prerequisite for agentic readiness.</p><h3>kubectl apply Was a Hint</h3><p>We’ve seen this pattern before.</p><p>kubectl apply didn’t simplify Kubernetes.</p><p>It changed the interface.</p><p>From:</p><blockquote>Step-by-step execution</blockquote><p>To intent:</p><blockquote><em>“Here is the desired state.”</em></blockquote><p>And the system:</p><ul><li>Builds the dependency graph</li><li>Determines ordering</li><li>Reconciles toward the desired state</li></ul><p>It doesn’t remove complexity.</p><p>It <strong>absorbs</strong> the graph.</p><h3>But We Never Measured the Absorption</h3><p>We say:</p><ul><li>Intent-driven</li><li>AI agents</li><li>Skills</li></ul><p>But we don’t ask:</p><blockquote><em>How much complexity moved from the developer to the system?</em></blockquote><p>That’s the <strong>gap</strong>.</p><h3>Intent Compression Ratio</h3><p>I’ve started calling this <strong>Intent Compression Ratio (ICR)</strong>:</p><blockquote>A way to measure how much execution complexity is collapsed into intent.</blockquote><p>To quantify this, we need a way to measure the “work” the system is doing for us.</p><ul><li><strong>Ops</strong> : Are the <em>total required operations</em> every distinct state-changing API call e.g. IAM binding, network rule, object creation, policy attachment, or validation step.</li><li><strong>Intent expressions</strong> are the <em>semantic</em> <em>goals</em> defined by the user.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/284/1*hFoqE86Oo4A_e5tH7LMujA.png" /></figure><p>A system with an ICR of 1 is just a wrapper.</p><p>A system with an ICR of 10 is an architectural partner.</p><p>At first glance, this looks like productivity.</p><p>It isn’t.</p><p>It measures who owns the system’s complexity.</p><h3>What Actually Gets Compressed</h3><p>Not just steps.</p><p><strong>Cognitive load</strong><br>The developer no longer tracks ordering or dependencies.</p><p><strong>Dependency graph</strong><br>The system constructs and executes the DAG.</p><p><strong>Desired state</strong><br>The system ensures correctness of what is being built, not just how.</p><p><strong>Failure surface</strong><br>Fewer invalid states. Fewer silent failures.</p><p>Higher ICR doesn’t just mean faster.</p><p>It means <strong>safer</strong> and more <strong>predictable</strong> systems.</p><h3>ICR in Action</h3><p>Let’s ground this with a few examples from the Snowflake platform.</p><p>Snowflake is a concrete example but <strong>Intent Compression Ratio is the general idea.</strong> I use <a href="https://docs.snowflake.com/en/user-guide/cortex-code/cortex-code-cli">Cortex Code</a> as an Agentic Coding tool<strong> proof point.</strong></p><p>The pattern is broader than any one platform: whenever developers have to coordinate object creation, dependency ordering, policy enforcement, and desired state, there is an opportunity to compress the graph into intent.</p><h3>Programmatic Access Token</h3><p>Intent:</p><blockquote><em>“Deploy a Google connector with </em><a href="https://docs.snowflake.com/en/user-guide/data-integration/openflow/about"><em>Openflow</em></a><em>.”</em></blockquote><p>Underlying reality:</p><p>1. Create service user<br>2. Create service role<br>3. Grant role to user<br>4. Create network rule<br>5. Create network policy<br>6. Attach network policy to user<br>7. Create authentication policy<br>8. Attach authentication policy to user<br>9. Configure PAT policy constraints<br>10. Generate PAT<br>11. Store PAT securely for client usage<br>12. Validate token exchange via <a href="https://nipyapi.readthedocs.io/en/latest/">Apache NiPyAPI</a></p><p><strong>ICR ≈</strong> <strong>12 Ops / 1 intent = 12</strong></p><p>Or expressed as intent compression:</p><p><strong>1 intent : 12 Ops</strong></p><p>This is not step reduction.</p><p>This is dependency graph compression.</p><h3>Network Access</h3><p>Intent:</p><blockquote><em>“Allow access to googleapis.com.”</em></blockquote><p>Underlying reality:</p><ol><li>Rule definition</li><li>Policy creation</li><li>Attachment</li><li>Grants</li></ol><p><strong>ICR ≈ 4 Ops / 1 intent = 4</strong></p><p>Or:</p><p><strong>1 intent : 4 Ops</strong></p><h3>External Volume</h3><p>Intent:</p><blockquote><em>“Set up an Iceberg table with Snowflake.”</em></blockquote><p>Underlying reality:</p><ol><li>Storage integration</li><li>IAM configuration</li><li>External bindings</li><li>Volume configuration</li><li>Access validation</li><li>Table setup</li></ol><p><strong>ICR ≈ 6 Ops / 1 intent = 6</strong></p><p>Or:</p><p><strong>1 intent : 6 Ops</strong></p><h3>Where Skills and AI Agents Fit</h3><p>This becomes critical in an AI-driven world.</p><p>AI agents are not good at:</p><ul><li>Long procedural chains</li><li>Fragile ordering</li><li>Implicit dependencies</li></ul><p>They are good at:</p><ul><li>Intent</li><li>Bounded abstractions</li><li>Repeatable execution units</li></ul><p>This is where skills come in.</p><p>A skill is not just automation.</p><p>It is a <strong>compressed execution graph with guarantees</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bS2kHIzCONuY1PNN8LHPWQ.png" /><figcaption><em>A skill compresses the execution graph behind a single expression of intent.</em></figcaption></figure><p>Whether implemented via Cortex Code or any other agentic system, a skill:</p><ul><li>Encodes ordering</li><li>Enforces constraints</li><li>Ensures the correct desired state</li><li>Handles sensitive steps, such as token handling, safely</li></ul><p>So when an agent executes:</p><blockquote><em>“Deploy a connector with Openflow.”</em></blockquote><p>It doesn’t reason through 12 Ops.</p><p>It invokes a single semantic unit.</p><p>ICR becomes a proxy for how usable a system is for agents.</p><h3>The Tradeoff</h3><p>Not all compression is good.</p><h3>Too Much Compression: Opaque Systems</h3><p>You run:</p><blockquote><em>“Deploy my connector.”</em></blockquote><p>It works — until it doesn’t.</p><p>Now the connector fails to authenticate.</p><p>You don’t know which role was created.<br>You don’t know which policy was applied.<br>You don’t know if the network rule exists.<br>You don’t know whether the PAT was generated under the right constraints.</p><p>You try to debug.</p><p>But there is no visibility into intermediate steps.<br>There is no way to fix just one part.<br>Everything is hidden behind the abstraction.</p><p>So you abandon it and go manual.</p><p>High ICR.<br>Zero observability.</p><p>That is not effective compression.</p><p>That is opacity.</p><h3>Too Little Compression: Fully Manual Systems</h3><p>Now take the opposite extreme.</p><p>You manually create everything:</p><ul><li>User</li><li>Role</li><li>Grants</li><li>Network rule</li><li>Network policy</li><li>Auth policy</li><li>Token</li></ul><p>Everything is visible.</p><p>But now you own everything.</p><p>You attach the wrong policy.<br>Or attach it in the wrong order.<br>Or bind the wrong role.<br>Or generate the token before the constraints are actually in place.</p><p>Nothing errors.</p><p>Your PAT is generated.<br>Your connector is deployed.</p><p>But it doesn’t work.</p><p>Debugging spans identity, network, and auth layers.</p><p>Full control.<br>Full responsibility.</p><p>That is not simplicity.</p><p>That is exposed complexity.</p><h3>Glass Box Compression</h3><p>The solution to opacity is not to go back to manual scripts.</p><p>It is observable intent.</p><p>I think of this as <strong>Glass Box Compression</strong>.</p><p>Unlike a black box, where the system performs magic and leaves you clueless when it breaks, a glass box system absorbs complexity while remaining transparent.</p><p>Now consider a well-designed skill:</p><blockquote><em>“Deploy a connector with Openflow.”</em></blockquote><p>It executes the full dependency graph.<br>It enforces ordering.<br>It validates desired state.<br>It exposes what it created.</p><p>And when something fails, it tells you:</p><ul><li>What failed</li><li>Why it failed</li><li>What was expected</li><li>What can be retried safely</li></ul><p>You can inspect the graph.<br>You can override specific parts.<br>You can re-run safely.</p><p>This is high ICR with visibility.</p><p>That is effective compression.</p><h3>The Shift</h3><p>We used to optimize for:</p><ul><li>Performance</li><li>Scale</li><li>Reliability</li></ul><p>Now we also optimize for:</p><blockquote><em>How much complexity the system can safely absorb.</em></blockquote><p>Because in an intent-driven world:</p><ul><li>Developers express goals</li><li>AI agents invoke abstractions</li><li>Systems execute graphs</li></ul><p>The interface moves closer to intent.</p><p>The execution moves deeper into the system.</p><p>And the value shifts from:</p><blockquote><em>“How many steps can I automate?”</em></blockquote><p>To:</p><blockquote><em>“How much complexity can I safely stop thinking about?”</em></blockquote><h3>Closing Thought</h3><p>The question is no longer:</p><blockquote><em>“What can this system do?”</em></blockquote><p>It is:</p><blockquote><em>“How much of the system can you safely not think about?”</em></blockquote><p>That is the real power of intent.</p><p>And that is what <strong>Intent Compression Ratio</strong> begins to measure.</p><p>This brings the series full circle.</p><p>In <a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14"><strong>Infrastructure as Intent</strong></a>, I argued that infrastructure is moving from imperative execution toward declarative intent: developers should describe the outcome, not manually orchestrate every step.</p><p>In <a href="https://blogs.kameshs.dev/the-ghost-in-the-machine-why-ai-needs-the-spirit-of-uml-0d8864e583e2"><strong>The Ghost in the Machine</strong></a>, I explored why intent still needs structure. We need the “Spirit of UML” — not necessarily UML diagrams themselves, but a way to model relationships, dependencies, and constraints before systems can safely automate them.</p><p>In <a href="https://blogs.kameshs.dev/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c"><strong>Intent-Driven Development</strong></a>, I looked at the developer shift: from writing every instruction to shaping the intent that AI systems and platforms can execute.</p><p><strong>Intent Compression Ratio</strong> connects those ideas.</p><p>If intent is the interface, and relationships are the hidden graph, then ICR asks:</p><blockquote><em>How much of that graph can the system safely absorb?</em></blockquote><p>That is the bridge from philosophy to measurement.</p><p>In the next part of this series, I’ll look at how to build high-ICR skills into custom AI agents.</p><h3>About the Author</h3><p>Kamesh Sampath is a Developer Advocate at Snowflake, an author, and a long-time open-source contributor with 25+ years in enterprise software. He works across data engineering and AI with developer communities, helping practitioners turn modern data platforms into systems that hold up in production.</p><p>Through talks, writing, and hands-on demos, Kamesh makes cloud, data, and AI topics easier to understand and apply — grounded in real-world constraints. His sessions mix deep technical detail with practical patterns that developers and data teams can apply right away.</p><p>Lately, he has been speaking about Apache NiFi, Snowflake Openflow, Snowflake Cortex, and PostgreSQL.</p><p>He believes technology becomes powerful when it is shared, taught, and built together.</p><p>GitHub | LinkedIn | Blog | X</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ceb6faf2e2f9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Intent Driven Development: The Shift Developers Can’t Ignore]]></title>
            <link>https://kameshsampath.medium.com/intent-driven-development-the-shift-developers-cant-ignore-ef434f94d56c?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/ef434f94d56c</guid>
            <category><![CDATA[developer-productivity]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Fri, 17 Apr 2026 03:46:01 GMT</pubDate>
            <atom:updated>2026-04-17T03:46:01.211Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Hqaw0Icoy4k0AasGUIMqWw.png" /><figcaption>Intent Driven Development</figcaption></figure><p><em>We spent decades writing code for machines. Now we need to express intent for them.</em></p><p>There’s a quiet anxiety in software development right now.</p><p>If AI can write code… where does that leave developers?</p><p>The answer is simpler and more uncomfortable than it seems.</p><p>Builders are not becoming obsolete.<br>They are becoming <strong>more important</strong>.</p><p>Because in a world where machines can generate code, the real bottleneck is no longer syntax.</p><p>It is <strong>clarity of intent</strong>.</p><p>AI is not replacing developers.<br>It is amplifying them.</p><p>A vague developer with AI produces noise.<br>A precise developer with AI produces systems.</p><p>The leverage has shifted but the responsibility has not.</p><p>Developers who can express intent are still in control.</p><p>But the interface has changed.</p><p>We are no longer just writing code.<br>We are <strong>shaping what systems should do with just pure natural language</strong>.</p><p>This shift doesn’t come out of nowhere.</p><p>My previous article on <a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14">Infrastructure as Intent</a>, I explored how systems are moving toward declared outcomes over explicit instructions.</p><p>In <a href="https://blogs.kameshs.dev/the-ghost-in-the-machine-why-ai-needs-the-spirit-of-uml-0d8864e583e2">The Spirit of UML</a>, I argued that abstraction is becoming essential again in the age of AI.</p><p>In this article, I propose that <strong>I</strong>ntent <strong>D</strong>riven <strong>D</strong>evelopment(IDD) is where these ideas meet in practice.</p><h3>We’re Still Thinking Like Syntaxists</h3><p>Even with AI tools everywhere, most developers still:</p><ul><li>Write code line by line</li><li>Focus on syntax and structure</li><li>Debug implementation details</li></ul><p>AI becomes:</p><ul><li>Faster autocomplete</li><li>Better Stack Overflow</li></ul><p>But that misses the point.</p><p>The real shift is not AI writing code.<br>It is:</p><p><strong>Developers expressing intent clearly enough that machines can realize it.</strong></p><h3>From Syntax to Semantics</h3><p>We were trained to be <em>syntaxists</em>.</p><p>We think in functions, APIs, and control flow.</p><p>Intent Driven Development requires semanticists:</p><ul><li>Intent</li><li>Constraints</li><li>Context</li><li>Outcomes</li></ul><p>Code doesn’t disappear.<br>But it stops being the starting point.</p><p><strong>Intent becomes the source. Code becomes the artifact.</strong></p><h3>Writing Better Prompts is Writing Better Software</h3><p>In this world, prompts are not casual inputs.<br>They are <strong>design artifacts</strong>.</p><p><strong>Weak:</strong></p><blockquote>Build a dashboard</blockquote><p><strong>Intent-driven:</strong></p><blockquote>Build a Streamlit dashboard using Snowflake TPCH data. Show revenue trends over time, allow filtering by region and customer segment, and generate natural language insights. Keep the UI minimal and ensure queries are efficient.</blockquote><p>The difference is <strong><em>precision</em></strong>.</p><h3>A Real Example: From Intent to App (Cortex + Streamlit)</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/371/1*mLKQSQ2Zog3Djt-NXGiURg.png" /><figcaption>Development Loop</figcaption></figure><p>Start with intent that clearly defines the end outcomes:</p><p><em>Build a Streamlit dashboard using Snowflake TPCH data.</em></p><ul><li><em>Show revenue trends over time</em></li><li><em>Allow filtering by region and customer segment</em></li><li><em>Use Cortex to generate natural language insights</em></li><li><em>Keep the UI minimal but interactive</em></li><li><em>Ensure queries are efficient and reusable</em></li></ul><p>No boilerplate. No step-by-step instructions just clear intents.</p><p>Using tools like Cortex Code, this intent can translate into:</p><ul><li>Snowflake queries over TPCH tables</li><li>Aggregations for revenue trends</li><li>A Streamlit UI with filters and charts</li><li>LLM-generated summaries like: “Revenue dipped in Q3 due to decline in APAC region”</li></ul><p>You didn’t handwrite everything.<br>You guided the system with intent.</p><h3>Where Developers Still Matter</h3><p>The first version won’t be perfect.</p><p>Maybe the query is inefficient.<br>Maybe the chart is unclear.<br>Maybe the insights are generic.</p><p>You refine the intent:</p><ul><li>Optimize queries for large datasets</li><li>Make insights specific to region trends</li><li>Add anomaly detection</li></ul><p>Each iteration improves the system without dropping to low-level code immediately.</p><h3>But… How Do You Know It’s Good?</h3><p>You build something like this and think:</p><p><em>“Looks right… but can I trust it?”</em></p><p>This is where most AI-driven apps fail.</p><p>Not in building but in <strong>knowing whether they’re accurate and efficient.</strong></p><h3>Adding a Evaluation Layer with TruLens</h3><p>Intent Driven Development needs evaluation.</p><p>Frameworks like TruLens make this practical.</p><p>They help measure whether your system aligns with your intent:</p><ul><li><strong>Context Relevance</strong> → Is the system finding the right data?</li><li><strong>Groundedness</strong> → Are insights tied to actual data?</li><li><strong>Answer Relevance</strong> → Is the system completing its goal?</li><li><strong>Plan Quality →</strong> Is the agent forming a plan that is optimal for the user’s goal?</li><li><strong>Tool Selection →</strong> Is the agent choosing the right tools for each task?</li><li><strong>Plan Adherence → </strong>Does the agent follow its own plan, or drift mid-execution?</li><li><strong>Tool Calling → </strong>Are tools being called correctly and with the right inputs?</li><li><strong>Logical Consistency → </strong>Does the agent maintain coherent reasoning across steps?</li><li><strong>Execution Efficiency → </strong>Is the agent solving the task with minimal steps, cost, and latency?</li></ul><p>These aren’t just metrics.</p><p>They are checks on your intent and the system’s ability to complete your goals.</p><h3>The New Loop</h3><p>Intent Driven Development changes the workflow:</p><ol><li>Define intent</li><li>Generate system</li><li>Measure results</li><li>Refine intent</li></ol><p>You’re not just debugging code.</p><p><strong>You’re debugging meaning.</strong></p><h3>Why This Matters Now</h3><ul><li>AI can act on intent</li><li>Systems are too complex to handcraft</li><li>The bottleneck is idea → implementation</li></ul><p>IDD reduces that gap.</p><p>It lets developers operate closer to the problem than the plumbing.</p><h3>The Developer Shift</h3><p>The next generation of developers will:</p><ul><li>Express constraints clearly</li><li>Structure intent precisely</li><li>Guide AI systems</li><li>Measure quality continuously</li></ul><p>The best developers won’t write the most code.</p><p>They’ll express the clearest intent and validate it.</p><h3>Closing</h3><p>We taught machines to understand syntax.</p><p>Now we need to lead them with intent.</p><p>AI hasn’t taken control away from developers.</p><p>It has raised the bar for what control means.</p><p>The developers who thrive in this shift won’t be the ones who write the most code.</p><p>They’ll be the ones who can:</p><ul><li>express intent clearly</li><li>define constraints precisely</li><li>And validate outcomes rigorously</li></ul><p>Because in this new model, code is no longer the source of truth.</p><p>Intent is.</p><p>AI is powerful but it is still just an executor.</p><p>The developer remains the authority.</p><p>And like any good system, the quality of the output will always depend on the clarity of the input.</p><p>It starts with something simple.</p><p><strong>The next intent you write.</strong></p><h3>Resources</h3><p>If you want to explore Intent Driven Development hands-on, here are a few starting points.</p><h4>Try the Intent Yourself</h4><p>Use this prompt with Cortex Code (or a similar tool) and iterate on it:</p><p><strong>Intent Prompt:</strong></p><p>Build a Streamlit dashboard using Snowflake TPCH data.</p><p>Requirements:</p><ul><li>Show revenue trends over time</li><li>Allow filtering by region and customer segment</li><li>Use semantic queries over Snowflake tables</li><li>Generate natural language insights for trends and anomalies</li><li>Ensure all insights are grounded in actual query results</li><li>Keep the UI minimal, fast, and interactive</li></ul><p>Constraints:</p><ul><li>Optimize queries for large datasets</li><li>Structure the code for reuse and clarity</li><li>Return both data visualizations and textual summaries</li></ul><p>Output:</p><ul><li>Streamlit app code</li><li>Snowflake queries used</li><li>Example insights generated from the data</li></ul><p>Start with this, then refine the intent.</p><p>That’s the practice: improving the clarity of what you want the system to do.</p><p>👉 Check out the TruLens cheatsheet: <a href="https://github.com/Snowflake-Labs/sf-cheatsheets/blob/main/rag-evaluation-cheatsheet.md">https://github.com/Snowflake-Labs/sf-cheatsheets/blob/main/rag-evaluation-cheatsheet.md</a></p><h3>Tools</h3><ul><li><a href="https://docs.snowflake.com/en/user-guide/cortex-code/cortex-code-cli">Cortex Code</a></li><li><a href="https://streamlit.io">Streamlit</a></li><li><a href="https://signup.snowflake.com/">Snowflake</a></li><li><a href="https://truelens.org">TruLens</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ef434f94d56c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Ghost in the Machine: Why AI Needs the Spirit of UML]]></title>
            <link>https://kameshsampath.medium.com/the-ghost-in-the-machine-why-ai-needs-the-spirit-of-uml-0d8864e583e2?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/0d8864e583e2</guid>
            <category><![CDATA[platform-engineering]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[infrastructure-as-code]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Sat, 28 Feb 2026 13:48:19 GMT</pubDate>
            <atom:updated>2026-02-28T13:48:19.115Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jByxnvBYt1QXzrh7zR4uyw.png" /></figure><h3>Introduction</h3><p>AI can generate code at 10x speed. It still doesn’t understand the systems it’s building.</p><p>This story is about why we need to revive the spirit of<a href="https://en.wikipedia.org/wiki/Unified_Modeling_Language"> Unified Modelling Language(<strong>UML</strong>)</a> — not as diagrams, but as living infrastructure.</p><p>Back then, the hot trend was<a href="https://en.wikipedia.org/wiki/Model-driven_engineering"> Model-Driven Development <strong>(MDD)</strong></a> — generating code from models. UML was the flagship: Class Diagrams, Sequence Diagrams, the whole catalog. The belief was simple: if the model was correct, the system would be correct.</p><p>We were wrong.</p><p>UML didn’t fail because modeling was useless. It failed because of the <strong>Sync Tax</strong>.</p><p>You draw a class diagram. Rational Rose generates clean code. You ship it. QA finds bugs. Hotfixes go in. Production diverges. The model is now stale.</p><p>Keeping diagrams and code aligned becomes real work. Expensive work. The tax exceeds the value. So teams stop paying it.</p><p>We declared, “The code is the documentation,” and moved on.</p><p>Starting to sound familiar?</p><p>If you ask me, we are back in the MDD era. We’ve just rebranded it as <strong>AI-Driven Development</strong>.</p><p>The diagram is gone. The model remains. Only now, instead of <strong>Sync Tax</strong>, we pay the <strong>Hallucination Tax</strong>.</p><p>AI agents generate code at unprecedented speed. Ask, “Generate a Snowflake pipeline to ingest customer events,” and it produces SQL, tasks, maybe even a clean dbt model in seconds.</p><p>But the real question is: Does it understand the system it is operating in?</p><p>Specifically, when you give it that prompt, does it see the whole picture?</p><ul><li>Your warehouse sizing and cost guardrails?</li><li>Your RBAC model and masking policies?</li><li>Which schema is production-certified versus experimental?</li><li>Which data is PII and which roles are allowed to see it?</li></ul><p>Then there’s the <strong>Context Gap</strong> — the ritual of re-explaining your infrastructure to an agent with limited working memory. Every new chat resets shared understanding. Yes, there are hacks. But none of them feel native.</p><p>The spirit of modeling was never about diagrams. It was about shared context.</p><p>AI has the muscle. It lacks shared context.</p><p>So here’s the twist: UML and AI are two halves of a broken whole.</p><p>UML was too rigid for humans to maintain. AI is too fluid for systems to trust.</p><p>To solve the<a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14"> <strong>Field Velocity</strong></a> problem, we don’t go back to 2005.</p><p>We make AI pay the <strong>Sync Tax</strong> for us.</p><p>And the only way to do that is to encode intent as an enforceable structure.</p><h3>The Living Model: From “Pretty Picture” to “Policy”</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aRpZbcEzcfYx26hKok6W_w.png" /><figcaption>Operationalizing <strong>Human Intent</strong>: Our conceptual model for combining an <strong>AI Agent</strong> with a central knowledge repository of <strong>Architectural Memory</strong> to enforce security, governance, and infrastructure best practices for automated and compliant code generation.”</figcaption></figure><p>In my earlier blog on <strong>I</strong><a href="https://blogs.kameshs.dev/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14"><strong>nfrastructure-as-Intent</strong></a>, I wrote about how human expertise and experience can be carved into agentic <strong>Skills</strong><em>(In Part 1, we defined Skills as the codified expertise and human experience that turn operators into mentors for AI agents.)</em><strong> </strong>, turning each of us from operators into mentors.</p><p>That shift forces us to stop authoring static models and start inferring living ones.</p><p>The truth of your system already exists — scattered across scripts, RBAC definitions, masking policies, and CI/CD pipelines.</p><p>So why does AI still hallucinate?</p><p>Because of the <strong>Context Gap</strong> — the absence of shared context.</p><p>Here is how we revive the UML spirit: by turning Skills into the rules of engagement for our AI agents.</p><h3>The Guardrail Effect</h3><p>When we practiced UML, we followed the structure. First system architecture. Then sequence diagrams. Then class diagrams. Step by step, we built a shared understanding.</p><p>In the AI era, shared understanding becomes shared constraints.</p><p>Instead of executing a prompt literally, the AI must query the Skills.</p><p>If a Skill declares, “All customer data requires masking primitive X,” that declaration becomes an enforceable structure. The agent generates within it, not around it. The constraint is applied at the point of creation.</p><p>That is the spirit of UML — reborn as an executable policy.</p><p>Policy is what machines can execute. Intent is what they can reason about.</p><h3>Semantic Reasoning</h3><p>Shared constraints unlock something UML never could: semantic reasoning.</p><p>UML provided syntactic structure. The reasoning lived in our heads.</p><p>AI can reason — but only if we give it structure.</p><p>Ask, “If I change this stream’s schema, who breaks?”</p><p>The AI uses the Skill graph to simulate impact and produce a blast-radius report before a single ALTER TABLE runs.</p><p>That is the difference between generation and understanding.</p><p>This is where intent stops being documentation and starts becoming infrastructure.</p><h3>Closing the Context Gap</h3><p>In my earlier post, I introduced the idea of a <strong>Shared Manifest</strong> — a persistent memory layer for AI agents. Not as a long prompt, but as structured Skills.</p><p>With Skills, the agent carries <strong>Architectural Memory</strong>.</p><p>It knows your certified schemas. Your cost guardrails. Your intent. Because it operates inside the same world model as you.</p><p>The Living Model is not documentation. It is infrastructure.</p><p>This is <strong>Infrastructure-as-Intent</strong> in action.</p><p>We use the rigidity of the model to keep AI honest.</p><p>We use the fluidity of AI to pay the <strong>Sync Tax</strong> — bringing the spirit of UML back as living infrastructure.</p><h3>A Call to Builders</h3><p>We don’t need a return to 2005-era static diagrams. We need an <strong>Architectural Memory</strong> for our systems.</p><p>Agentic AI tools have already defined the schema. Don’t panic about learning another DSL 🤣. It’s not one.</p><p>It’s a disciplined Markdown. Clear English. Proper structure. The same rigor you apply when writing a design doc.</p><p>Except now, that document is executable.</p><p>It’s not documentation for humans alone. It is codified wisdom for agents.</p><p>The era of blind velocity is over.</p><p>It’s time to give the muscle a brain.</p><blockquote><em>Visuals note: The conceptual diagrams in this post were generated by Google Gemini.</em></blockquote><h3>About the Author</h3><p><strong><em>Kamesh Sampath</em></strong><em> is a Lead Developer Advocate at Snowflake, author, and long-time open-source contributor with 25+ years in enterprise software. He works across data engineering and AI with developer communities, helping practitioners turn modern data platforms into systems that hold up in production.</em></p><p><em>Through talks, writing, and hands-on demos, Kamesh makes cloud, data, and AI topics easier to understand and apply — grounded in real-world constraints. His sessions mix deep technical detail with practical patterns that developers and data teams can apply right away.</em></p><p><em>Lately, he’s been speaking about Apache NiFi (Snowflake Openflow), AI (Snowflake Cortex), and PostgreSQL.</em></p><p><em>He believes technology becomes powerful when it is shared, taught, and built together.</em></p><p><a href="https://github.com/kameshsampath">GitHub</a> |<a href="https://linkedin.com/in/kameshsampath"> LinkedIn</a> |<a href="https://blogs.kameshs.dev/"> Blog</a> |<a href="https://x.com/kamesh_sampath"> X</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0d8864e583e2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Infrastructure-as-Intent: The Field Velocity Blueprint]]></title>
            <link>https://kameshsampath.medium.com/infrastructure-as-intent-the-field-velocity-blueprint-e6217ef30f14?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/e6217ef30f14</guid>
            <category><![CDATA[snowflake]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[generative-ai]]></category>
            <category><![CDATA[infrastructure-as-code]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Mon, 16 Feb 2026 16:45:10 GMT</pubDate>
            <atom:updated>2026-03-17T07:25:34.479Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3lormjg3wxxIAT68HMV4tQ.png" /><figcaption>Infrastructure-as-Intent: Bridging human creativity with AI precision to accelerate data value.</figcaption></figure><p>We ’ve spent years building automation pipelines to deploy infrastructure. We call it Infrastructure-as-Code (IaC) — whether that’s Terraform, Ansible, or even simple CLI scripts. It saves us from nightmares when we need consistency between where we develop and where we deploy. <strong>Development == Production</strong>.</p><p>But when Snowflake launched <a href="https://www.snowflake.com/en/product/features/cortex-code/">Cortex Code</a> — an AI coding agent designed for the Data Cloud — it sparked a new thought for me: why can’t we take IaC automation into its next phase? Instead of declaring how to build everything step by step, what if we simply declared the outcome we want?</p><p>This idea wasn’t born out of nowhere. It came from my own attempts to modernize my scripts — <a href="https://github.com/kameshsampath/snow-utils">snow-utils</a> — a collection of utilities I built to repeatedly set up Snowflake environments for demos. They started as a way to eliminate the rinse-and-repeat plumbing work.</p><p>The scripts solved the plumbing problem. But they introduced a different one: <em>remembering the right combination of parameters every single time</em>.</p><p>That frustration led me to a deeper pattern — one that could fundamentally change how we interact with cloud environments, and one that fits naturally into this new AI-assisted era.</p><h3>The “Field Velocity” Problem</h3><p>So we found the problem, built a solution, and then refined it further — something that could take us into the next phase. But before we get there, let’s pause and ask: why does this even matter?</p><p>The answer is simple and universal: <strong>speed</strong>.</p><p>We invented computers to help us do things faster. But as computer science evolved, the systems we built became increasingly complex. Somewhere along the way, speed started fighting complexity.</p><p>That’s what I call the <strong>Field Velocity problem</strong>.</p><p>What is it exactly?</p><p>It’s the time it takes to go from a customer asking:</p><blockquote>Can we query Snowflake-managed Iceberg tables from DuckDB?</blockquote><p>to a working, deployed proof of concept.</p><p>If you look closely at the history of Infrastructure-as-Code, you’ll notice something consistent. The bottleneck is rarely the idea. It’s the plumbing.</p><p>So our customer’s question will translate into following tasks(plumbing):</p><blockquote>Configuring IAM roles – &gt; Editing trust policies – &gt; Setting up buckets. – &gt; Creating network rules – &gt; DDL Statements.</blockquote><p>If I put it mathematically, we spend 90% of our time on plumbing and only 10% on the actual value.</p><p>So the fix? Invert the ratio.</p><p>That inversion is what I call <strong>Infrastructure-as-Intent</strong>.</p><p>Same wine. New bottle. 😉</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_U1KG3kNJDo7nn5CtLVzzQ.png" /><figcaption>Field Velocity: Flipping the ratio with Infrastructure as Intent</figcaption></figure><h3>The Evolution: <strong>From Power CLI to Atomic Primitives</strong></h3><p>I come from a strong Linux and infrastructure background, and I’m a die-hard CLI person. If something is repetitive, my instinct is to script it.</p><p>That “Power CLI” reflex is what led me to write snow-utils.</p><p>Let me explain with a simple example.</p><p>Say I want to connect Snowflake to an AWS S3 bucket. Sounds straightforward. In reality, it looks like this:</p><ul><li>Log into AWS Console → Create S3 bucket</li><li>Create an IAM policy</li><li>Create an IAM role and attach the policy</li><li>Log into Snowflake → Create the EXTERNAL VOLUME</li><li>Run DESCRIBE EXTERNAL VOLUME to hunt for AWS user ARN</li><li>Go back to AWS → Edit the Trust Relationship JSON and paste those values</li></ul><p>And yes — to err is human. But one small typo in that trust policy JSON, or grabbing the wrong property from the Snowflake DESCRIBE output, can cost you hours of debugging before everything finally works.</p><p>That’s when I decided to wrap this entire chain of interdependent steps into a single command.</p><p>Instead of manually wiring everything together, I created a primitive that understood the sequence.</p><p>Now, for the same task, it’s simply:</p><pre>snow-utils extvolume:up</pre><p>Done.</p><p>One clean shot. Goal scored.</p><p>Over time, it became deterministic. It worked every time — no manual hunting, no fragile copy-paste, no mysterious “Access Denied” errors.</p><p>That’s when I realized something important: these weren’t just scripts anymore. They were <strong>atomic primitives</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nxGMdB_DGn5mXHk7cmR78g.png" /><figcaption>Deconstruction: Moving from brittle, monolithic scripts to composable, reusable primitives.</figcaption></figure><h3>The Shift: From Operator to Mentor</h3><p>Once I had the primitives working reliably, I started asking a new question: why can’t this be more intelligent?</p><p>Around that time, Snowflake announced Cortex Code. What caught my attention wasn’t just that it was an AI coding agent, but the concept of <strong>Skills</strong> — giving the agent structured capabilities to orchestrate tools and perform tasks in a more contextual way.</p><p>That’s when I saw the transition from Infrastructure-as-Code (IaC) to Infrastructure-as-Intent (IaI) taking shape.</p><p>Instead of keeping a mental checklist of parameters, flags, expiration rules, and policy combinations, why couldn’t I just express the intent and let the system handle the wiring?</p><blockquote>“Set up a Snowflake External Volume.”<br>“Create a Snowflake PAT.”</blockquote><p>It suddenly felt possible.</p><p>But this wasn’t just about making things simpler. It was about changing roles.</p><p>With IaC, I was the operator — running commands, supplying parameters, ensuring everything aligned.</p><p>With IaI, I started becoming <strong>the</strong> mentor.</p><p>I wasn’t just teaching syntax. I was embedding experience.</p><p>Years of small production scars. Lessons from silent authentication failures. The instinct to validate IAM structures before applying them. The habit of forcing a dry run before destructive changes. The discipline of demanding explicit confirmation when something could break environments.</p><p>That’s what I call <strong>codifying wisdom</strong> — capturing operational judgment and passing it into the system so it behaves the way I would.</p><p>Let’s not fear AI. It doesn’t invent wisdom on its own — it executes what we mentor it to do.</p><p>Intelligence still comes from somewhere.</p><p>Let me make this concrete.</p><p>Suppose I want to create a Snowflake <strong>Programmatic Access Token (PAT)</strong>. Traditionally, that involves multiple careful steps: creating a service user, applying network restrictions, attaching the correct policies, defining expiration windows, and ensuring everything aligns securely.</p><p>Instead of remembering every option and edge case, I wrote a simple markdown file called SKILLS and embedded all the logic behind the PAT primitive into it — including the commands and my opinions about how it should be done safely.</p><p>Now all I need to tell Cortex Code is:</p><blockquote>“Set me up a PAT that’s valid for 7 days and expires 30 days from now.”</blockquote><p>That’s it.</p><p>No more remembering long chains of parameters. No wrappers around wrappers. No cognitive overhead.</p><p>What emerged was a <strong>composable architecture</strong> — where the logic (snow-utils) is unbundled from the interface (Cortex Code).</p><ul><li>The <strong>primitives</strong> remain solid.</li><li>The <strong>skills</strong> carry the experience.</li><li>The interface interprets <strong>intent</strong>.</li></ul><p>That separation is what turns automation into intent.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZotcbDDpEap36_9bVsHRLg.png" /><figcaption>The New Workflow: The Human defines the “Skill” (intent &amp; guardrails), and the AI handles the execution.</figcaption></figure><h3>The Pedagogical Pattern: Intentional Failure as a Feature</h3><p>When I spoke about intent in the previous section, I didn’t just mean my intent to run a set of commands or automation scripts. I had another intent in mind as well.</p><p>Too many intents? I can already see an “Intent Overflow Exception” coming. 😉</p><p>Jokes apart — as a Power CLI user, clear intent helps me execute tasks efficiently. But a mentor does more than execute. A mentor creates situations where you apply your skills across related areas. A mentor shapes understanding.</p><p>As a Developer Advocate, I build demos, tutorials, and utilities like snow-utils. I usually share my GitHub repositories with a README explaining how to run the demo you just saw in my session.</p><p>That works.</p><p>But nothing matches personalized teaching — where the session leader’s experience, empathy, and small nuances surface naturally. The little warnings. The subtle explanations. The “watch this part carefully” moments.</p><p>That’s a classic pedagogical pattern.</p><p>Let me explain with the <a href="https://github.com/kameshsampath/hirc-duckdb-demo">Horizontal Iceberg REST Catalog (HIRC) and DuckDB demo</a>.</p><p>All you have to say now is:</p><blockquote>“Deploy HIRC DuckDB demo.”</blockquote><p>But the real power isn’t just in deploying the environment. It’s in how the Skill is structured.</p><p>I intentionally embedded a learning flow into it:</p><ul><li>Say what you are going to do</li><li>Do it</li><li>Let it fail</li><li>Explain why it failed</li><li>Fix it</li><li>Show how it succeeds</li></ul><p>That pattern is deliberate.</p><p>The goal isn’t just automation. It’s understanding.</p><p>That’s the human part — and that doesn’t emerge automatically. You have to mentor it into the system.</p><p>Now the Skill doesn’t just deploy infrastructure. It delivers a learning experience.</p><h3>The “Recorder” Bonus: Intelligent &amp; Interactive Reproduction</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KOu3CSQFWrAkIcgopzrMnQ.png" /><figcaption>Context Adaptation: Transforming a manifest from “Works on my machine” (AWS) to “Works on your cloud” (Azure)</figcaption></figure><p>Since I’m documenting intent as a Skill, the natural next step was to share it.</p><p>That’s how we normally share knowledge — notes, blogs like this, repositories, configuration files.</p><p>So I asked myself: what if I wanted my product team to reproduce the exact same demo? Or reproduce a tricky bug?</p><p>In a classic IaC world, I would share a configuration file. The other person would clone the repo, pull it down, and then start adjusting dozens of environment settings — region values, parameter overrides, warehouse sizes, credentials — before they could even run it.</p><p>It works, but it assumes their environment matches mine.</p><p>Now think about how humans do this.</p><p>If I explain a setup to you, you don’t copy it blindly. You adapt it to your context. You change the region. You adjust the resource sizes. You map it to your naming conventions. You interpret the intent, not just the configuration.</p><p>That’s exactly what the IaI pattern enables with shared manifests.</p><p>When I share a manifest, I’m not just sharing a file. I’m sharing intent.</p><p>That intent can then be materialized in your environment — with adaptation.</p><p>Same architectural goal. Different context.</p><p>This isn’t blind automation like traditional IaC. It’s careful intent materialization.</p><p>And that difference is subtle — but important.</p><h3>The Future: The Velocity Catalog</h3><p>What we’ve discussed so far is not just another pattern or a personal experiment. It’s a mental shift we need to make in this AI era.</p><p>snow-utils is simply an example of how this shift took shape for me. The same idea can apply to any tool or script.</p><p>The real opportunity is this: we need to mentor AI to think and act the way we would — embedding our experience, judgment, and operational discipline into it.</p><p>Maybe that’s the next step for us: inject intent into DevOps.</p><h3>Resources</h3><p>The HIRC demo is just one example. Here is the <strong>4-minute time-lapse</strong> demo that shows how this pattern works:</p><iframe src="https://cdn.embedly.com/widgets/media.html?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DZbNjuoCkKeY&amp;type=text%2Fhtml&amp;schema=youtu&amp;display_name=YouTube&amp;src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FZbNjuoCkKeY" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/55888452d87c833abe5e00028915a697/href">https://medium.com/media/55888452d87c833abe5e00028915a697/href</a></iframe><p>The same pattern applies across workloads. In the demo repository, I’ve also included the <strong>AI Smart Crowd Counter</strong>.</p><p>All you have to say is:</p><p>“Deploy AI Smart Crowd Counter.”</p><p>The underlying primitives remain the same. The intent changes.</p><p>One moment you’re setting up a data catalog. The next, you’re provisioning a Streamlit app and an image-processing pipeline.</p><p>Same intent model. Different outcome.</p><p>That’s the leverage.</p><p>If you’re experimenting with AI agents in your DevOps workflows, start by stabilizing your primitives. Once they’re solid, you can begin layering Skills on top of them.</p><p>And from there, you can build your own Velocity Catalog.</p><p>For reference, the underlying orchestration layer used here is Snowflake Cortex Code:</p><p>👉 <a href="https://docs.snowflake.com/en/user-guide/cortex-code/cortex-code-cli">Snowflake Cortex Code CLI Documentation</a></p><blockquote>Visuals note: The conceptual diagrams in this post were generated by Google Gemini.</blockquote><h3>About the Author</h3><p><strong><em>Kamesh Sampath</em></strong><em> is a Lead Developer Advocate at Snowflake, author, and long-time open-source contributor with 25+ years in enterprise software. He works across data engineering and AI with developer communities, helping practitioners turn modern data platforms into systems that hold up in production.</em></p><p><em>Through talks, writing, and hands-on demos, Kamesh makes cloud, data, and AI topics easier to understand and apply — grounded in real-world constraints. His sessions mix deep technical detail with practical patterns that developers and data teams can apply right away.</em></p><p><em>Lately, he’s been speaking about Apache NiFi (Snowflake Openflow), AI (Snowflake Cortex), and PostgreSQL.</em></p><p><em>He believes technology becomes powerful when it is shared, taught, and built together.</em></p><p><a href="https://github.com/kameshsampath">GitHub</a> |<a href="https://linkedin.com/in/kameshsampath"> LinkedIn</a> |<a href="https://blogs.kameshs.dev/"> Blog</a> |<a href="https://x.com/kamesh_sampath"> X</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e6217ef30f14" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I Built a Document Search Service in 30 Seconds (And You Can Too) With Snowflake Cortex]]></title>
            <link>https://kameshsampath.medium.com/i-built-a-document-search-service-in-30-seconds-and-you-can-too-with-snowflake-cortex-231f83edd22d?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/231f83edd22d</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[snowflake]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[data-engineering]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Tue, 08 Jul 2025 12:22:04 GMT</pubDate>
            <atom:updated>2025-07-11T16:06:24.640Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Transform your document pipeline from reactive batch processing to intelligent real-time automation</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*duYx7h3cGlF5ip5l" /><figcaption>Photo by <a href="https://unsplash.com/@duforddigital?utm_source=medium&amp;utm_medium=referral">MJ Duford</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>In today’s fast-paced digital landscape, organizations demand immediate access to information locked inside documents. Traditional batch processing pipelines introduce frustrating delays — users upload files but must wait for scheduled jobs before their data becomes searchable. This lag not only slows down business operations but also wastes resources and complicates error handling.</p><p><strong>What if you could build a document processing pipeline that reacts instantly to new uploads?</strong></p><p>Imagine a system where every PDF, invoice, or contract is detected, parsed, and made searchable within seconds — without manual intervention or complex scheduling.</p><p>This article shows you how to architect such an intelligent, real-time pipeline using <a href="https://docs.snowflake.com/en/user-guide/streams-intro">Snowflake Streams</a> and <a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/aisql">Cortex AI</a>.</p><h3>What You’ll Learn</h3><p>By the end of this guide, you’ll know how to:</p><p>• <strong>Instantly detect new file uploads</strong> with zero polling<br> • <strong>Extract and chunk document content</strong> using AI-powered parsing<br> • <strong>Enable semantic search</strong> with vector embeddings<br> • <strong>Automate the entire workflow</strong> for reliability and scalability</p><p>Whether you’re modernizing invoice processing, legal document search, or any workflow that relies on fast document access, this guide will help you move from slow, error-prone batches to a responsive, AI-driven solution.</p><h3>The Real Problem: When File Processing Takes Forever</h3><p>Picture this: You’re building a document search system for invoice processing. Your finance team uploads an invoice PDF at <strong>10:15 AM</strong>, but they can’t search it until <strong>11:30 AM</strong> because your system processes files in hourly batches.</p><p>This is the reality for most organizations today:</p><pre>-- Traditional batch processing (runs every hour)<br>SELECT * FROM documents <br>WHERE upload_time &gt; DATEADD(hour, -1, CURRENT_TIMESTAMP());</pre><h3>The Pain Points That Kill Productivity</h3><p><strong>1. Massive Latency</strong></p><ul><li>User uploads invoice at <strong>10:15 AM</strong></li><li>Batch job runs at <strong>11:00 AM</strong></li><li>Invoice becomes searchable at <strong>11:30 AM</strong></li><li><strong>User waits 1 hour 15 minutes!</strong></li></ul><p><strong>2. Resource Waste</strong></p><ul><li>Batch processing scans everything</li><li>Scans all files in the stage (e.g., 10,000 files)</li><li>Only a few files (e.g., 5) are actually new since the last run</li><li>Still processes all 10,000 files, wasting compute and time</li></ul><p><strong>3. Complex Failure Handling</strong> When one document fails in a batch of 1000, what happens?</p><ul><li>Skip it and lose the document?</li><li>Retry the entire batch?</li><li>Build complex error recovery logic?</li></ul><p><strong>4. Cost Explosion</strong></p><ul><li><strong>AI Processing</strong>: Called on all documents, not just new ones</li><li><strong>Compute</strong>: Runs large warehouses even for tiny changes</li><li><strong>Developer Time</strong>: Complex scheduling and monitoring</li></ul><h3>Why Streams Change Everything</h3><p>This is where <strong>Snowflake Streams</strong> transform file processing. Instead of periodically scanning for changes, Streams automatically detect file uploads the moment they happen:</p><pre>-- Stream automatically detects new documents<br>CREATE STREAM document_changes ON STAGE document_storage;</pre><p>Then when you upload a PDF to the stage say “Invoice 1045.pdf” from then the query like the following will return the inserted document.</p><pre>-- Only process actual changes<br>SELECT relative_path, METADATA$ACTION FROM document_changes;<br>-- Returns: &quot;Invoice 1045.pdf&quot;, &quot;INSERT&quot; (instantly when uploaded)</pre><p><strong>The magic</strong>: Zero polling, zero scanning, instant detection.</p><h3>Our Solution: Real-Time Processing Pipeline</h3><p>Here’s what we’ll build to solve these problems:</p><ol><li><strong>Instant Detection</strong>: Stream detects file upload immediately</li><li><strong>AI Extraction</strong>: Cortex parses PDF and extracts text</li><li><strong>Smart Chunking</strong>: Breaks content into searchable segments</li><li><strong>Vector Embeddings</strong>: Creates semantic search capability</li><li><strong>Real-time Search</strong>: Document searchable in ~30 seconds</li></ol><p>Let’s build it step by step using real invoice PDFs.</p><h3>Setting Up the Foundation</h3><blockquote><strong>NOTE</strong>:</blockquote><blockquote>If you dont have a Free Snowflake trial, get one signed up <a href="https://bit.ly/snow-india-meetups">here</a> . Please make sure you use AWS us-west-2 and Enterpise edition for the code snippets to work as-is</blockquote><p>First, create the infrastructure for file processing:</p><pre>-- Create database and warehouse<br>CREATE DATABASE IF NOT EXISTS PDF_PROCESSOR;<br>CREATE WAREHOUSE IF NOT EXISTS FILE_WH WAREHOUSE_SIZE = SMALL;</pre><pre>USE DATABASE PDF_PROCESSOR;<br>USE WAREHOUSE FILE_WH;<br>-- Stage for PDF storage with directory tables enabled<br>CREATE OR REPLACE STAGE pdf_docs<br>  ENCRYPTION = (TYPE = &#39;SNOWFLAKE_SSE&#39;) <br>  DIRECTORY = (ENABLE = true);<br>-- Streams to detect file changes<br>CREATE OR REPLACE STREAM docs_inserted ON STAGE pdf_docs;<br>CREATE OR REPLACE STREAM docs_deleted ON STAGE pdf_docs;</pre><p>🔑 <strong>Key Point</strong>: DIRECTORY = (ENABLE = true) automatically tracks file metadata and enables change detection on the stage.</p><h3>Creating the AI Processing Pipeline</h3><p>Next, set up tables and the AI processing logic:</p><pre>-- Table to store processed document chunks<br>CREATE OR REPLACE TABLE document_chunks (<br>    file_name VARCHAR,<br>    chunk_text VARCHAR,<br>    chunk_number INT,<br>    processed_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP()<br>);</pre><pre>-- Stored procedure for AI-powered processing<br>CREATE OR REPLACE PROCEDURE process_new_documents()<br>RETURNS VARCHAR<br>LANGUAGE SQL<br>AS<br>$$<br>BEGIN<br>    -- Handle deleted documents first<br>    DELETE FROM document_chunks<br>    USING docs_deleted<br>    WHERE document_chunks.file_name = docs_deleted.relative_path<br>    AND docs_deleted.METADATA$ACTION = &#39;DELETE&#39;;<br>    -- Process new documents with AI<br>    CREATE OR REPLACE TEMPORARY TABLE parsed_docs AS<br>    SELECT<br>        relative_path,<br>        SNOWFLAKE.CORTEX.PARSE_DOCUMENT(<br>            &#39;@pdf_docs&#39;,<br>            relative_path,<br>            {&#39;mode&#39;: &#39;LAYOUT&#39;}<br>        ):content AS extracted_text<br>    FROM docs_inserted<br>    WHERE METADATA$ACTION = &#39;INSERT&#39;;<br>    -- Intelligent chunking for better search<br>    INSERT INTO document_chunks (file_name, chunk_text, chunk_number)<br>    SELECT<br>        relative_path,<br>        chunk.value,<br>        chunk.index<br>    FROM parsed_docs,<br>    LATERAL FLATTEN(<br>        input =&gt; SNOWFLAKE.CORTEX.SPLIT_TEXT_RECURSIVE_CHARACTER(<br>            extracted_text,<br>            &#39;markdown&#39;,<br>            1500,  -- chunk size<br>            200    -- overlap<br>        )<br>    ) chunk;<br>    RETURN &#39;Processed &#39; || (SELECT COUNT(DISTINCT file_name) FROM document_chunks) || &#39; documents&#39;;<br>END;<br>$$;</pre><p>This procedure does three critical things:</p><ol><li><strong>Cleans up</strong> deleted documents automatically</li><li><strong>Extracts text</strong> using Cortex AI’s PARSE_DOCUMENT function</li><li><strong>Creates intelligent chunks</strong> optimized for search</li></ol><h3>Automation with Smart Tasks</h3><p>Now make it fully automated with intelligent triggering:</p><pre>-- Serverless Task runs when either stream has data<br>CREATE OR REPLACE TASK auto_process_docs<br>    SCHEDULE = &#39;1 MINUTE&#39;<br>WHEN<br>    SYSTEM$STREAM_HAS_DATA(&#39;docs_inserted&#39;) <br>    OR SYSTEM$STREAM_HAS_DATA(&#39;docs_deleted&#39;)<br>AS<br>    CALL process_new_documents();</pre><pre>-- Enable the task<br>ALTER TASK auto_process_docs RESUME;</pre><h3>Why Two Separate Streams?</h3><p>Streams become empty once their data is consumed in a DML operation. If we used a single stream for both INSERT and DELETE operations, consuming one type of change would empty the entire stream, potentially losing the other type of changes.</p><p>By using two streams:</p><ul><li>docs_inserted captures only INSERT operations (new files)</li><li>docs_deleted captures only DELETE operations (removed files)</li><li>Each can be consumed independently without affecting the other</li></ul><p>The task triggers for:</p><ul><li><strong>File uploads</strong> (docs_inserted has data) → Process new documents</li><li><strong>File deletions</strong> (docs_deleted has data) → Clean up old chunks</li><li><strong>Both operations</strong> → Handle mixed scenarios</li></ul><p>This ensures your pipeline stays perfectly synchronized with the actual files in the stage.</p><h3>Testing with Real Data</h3><p>Let’s test with sample invoice PDFs. Use <a href="https://app.snowflake.com/sfdevrel/sfdevrel_enterprise/#/data/add-data/?from_deeplink=true&amp;utm_campaign=-us-en-all&amp;utm_content=-app-snowflake-streams-%20-ai%3A-from-upload-to-searchable-in-30%C2%A0seconds&amp;utm_medium=blog&amp;utm_source=medium_blog">Snowsight Load Files into a Stage</a> feature to upload invoice files form <a href="https://github.com/kameshsampath/snowflake-streams-ai-pipeline/tree/main/scripts/data">./scripts/data</a> folder. Say for example upload the following invoices “Invoice 1045.pdf”, “Invoice 2348.pdf” and “Invoice 3943.pdf”.</p><p>Check if the stream detected them:</p><pre>-- See what the stream captured<br>SELECT <br>    relative_path,<br>    size,<br>    last_modified,<br>    METADATA$ACTION<br>FROM docs_inserted;</pre><p>You should see ( other columns trimmed for brevity):</p><pre>RELATIVE_PATH   | SIZE |  LAST_MODIFIED             | METADATA$ACTION<br>Invoice 1045.pdf|58850|2025-06-20 07:11:40.000 -0700|INSERT<br>Invoice 2348.pdf|59213|2025-06-20 07:11:40.000 -0700|INSERT<br>Invoice 3943.pdf|59008|2025-06-20 07:11:40.000 -0700|INSERT</pre><p>Within 1–2 minutes, the automated task will process these files. View the <a href="https://app.snowflake.com/_deeplink/#/compute/history/tasks?preset=PRESET_LAST_DAY&amp;type=relative&amp;relative=%7B%22tense%22%3A%22past%22%2C%22value%22%3A1%2C%22unit%22%3A%22day%22%2C%22excludePartial%22%3Afalse%2C%22exclusionSize%22%3A%22day%22%2C%22exclusionSizeParam%22%3A%22%22%7D&amp;startDate=2025-06-19T00%3A00%3A00Z&amp;endDate=2025-06-20T00%3A00%3A00Z&amp;view=taskGraph&amp;database=PDF_PROCESSOR&amp;schema=PUBLIC">Task History</a> for the actual status. Check the results:</p><pre>-- View processed chunks<br>SELECT <br>    file_name,<br>    COUNT(*) as chunk_count,<br>    AVG(LENGTH(chunk_text)) as avg_chunk_size<br>FROM document_chunks<br>GROUP BY file_name;</pre><h3>Adding Intelligent Search</h3><p>Now add semantic search capabilities:</p><pre>-- Create search service with vector embeddings<br>CREATE OR REPLACE CORTEX SEARCH SERVICE invoice_search_service<br>    ON chunk_text<br>    WAREHOUSE = &#39;FILE_WH&#39;<br>    TARGET_LAG = &#39;1 MINUTE&#39;<br>    EMBEDDING_MODEL = &#39;snowflake-arctic-embed-l-v2.0&#39;<br>AS (<br>    SELECT file_name, chunk_text<br>    FROM document_chunks<br>);</pre><p>Once the Search Service has been created, you can find the status of the same via the <a href="https://app.snowflake.com/_deeplink/#/cortex/search/databases/PDF_PROCESSOR/schemas/PUBLIC/services/INVOICE_SEARCH_SERVICE/service">Search Service Listing</a> in Snowsight.</p><h3>Query the Search Service via REST API</h3><p>Get the <a href="https://docs.snowflake.com/en/developer-guide/sql-api/authenticating#label-sql-api-authenticating-key-pair">Snowflake Keypair Authentication</a> JWT keys, generated using <a href="https://docs.snowflake.com/en/developer-guide/snowflake-cli/index">Snowflake CLI</a>:</p><pre>export CORTEX_SEARCH_JWT=$(snow connection generate-jwt)</pre><blockquote><strong>TIP</strong>:</blockquote><blockquote>You can also use <a href="https://docs.snowflake.com/en/user-guide/programmatic-access-tokens">Snowflake Programmatic Access Token</a>(PAT) over JWT . The repository has a <a href="https://github.com/kameshsampath/snowflake-streams-ai-pipeline/blob/main/scripts/pat.sh">pat.sh </a>that can be used to generate Service User with PAT.</blockquote><p>For production applications, use the REST API to search your documents:</p><pre># Search for payment-related content<br>curl --location https://your-account.snowflakecomputing.com/api/v2/databases/PDF_PROCESSOR/schemas/PUBLIC/cortex-search-services/invoice_search_service:query \<br>    --header &#39;X-Snowflake-Authorization-Token-Type: KEYPAIR_JWT&#39; \<br>    --header &#39;Content-Type: application/json&#39; \<br>    --header &#39;Accept: application/json&#39; \<br>    --header &quot;Authorization: Bearer $CORTEX_SEARCH_JWT&quot; \<br>    --data &#39;{<br>        &quot;query&quot;: &quot;payment terms and due dates&quot;,<br>        &quot;columns&quot;: [&quot;chunk_text&quot;, &quot;file_name&quot;],<br>        &quot;limit&quot;: 5<br>    }&#39;</pre><pre># Search for vendor information<br>curl --location https://your-account.snowflakecomputing.com/api/v2/databases/PDF_PROCESSOR/schemas/PUBLIC/cortex-search-services/invoice_search_service:query \<br>    --header &#39;X-Snowflake-Authorization-Token-Type: KEYPAIR_JWT&#39; \<br>    --header &#39;Content-Type: application/json&#39; \<br>    --header &#39;Accept: application/json&#39; \<br>    --header &quot;Authorization: Bearer $CORTEX_SEARCH_JWT&quot; \<br>    --data &#39;{<br>        &quot;query&quot;: &quot;supplier vendor company name&quot;,<br>        &quot;columns&quot;: [&quot;chunk_text&quot;, &quot;file_name&quot;],<br>        &quot;limit&quot;: 5<br>    }&#39;</pre><h3>Alternative: SQL-based Testing</h3><p>For quick testing within Snowflake:</p><pre>-- query the document chunks<br>SELECT PARSE_JSON(<br>  SNOWFLAKE.CORTEX.SEARCH_PREVIEW(<br>      &#39;invoice_search_service&#39;,<br>      &#39;{<br>         &quot;query&quot;: &quot;payment terms and due dates&quot;,<br>         &quot;columns&quot;:[<br>            &quot;file_name&quot;,<br>            &quot;chunk_text&quot;<br>         ],<br>         &quot;filter&quot;: {&quot;@eq&quot;: {&quot;file_name&quot;: &quot;Invoice 2348.pdf&quot;} },<br>         &quot;limit&quot;:5<br>      }&#39;<br>  )<br>)[&#39;results&#39;] as results;</pre><h3>Advanced AI Features</h3><p>Extract structured data using <a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/aisql">Cortex AISQL</a> functions:</p><pre>-- Extract key invoice information<br>SELECT <br>    file_name,<br>    SNOWFLAKE.CORTEX.COMPLETE(<br>        &#39;claude-4-sonnet&#39;,<br>        CONCAT(&#39;What is the invoice number and total amount? Use the context between &lt;context&gt;.Format as markdown.&#39;, chunk_text,&#39;&lt;/context&gt;&#39;)<br>    ) as invoice_and_total<br>FROM document_chunks<br>LIMIT 10;</pre><h3>Production Optimizations</h3><p>For production workloads, consider these enhancements:</p><h4>Smart Warehouse Sizing</h4><pre>-- Different warehouses for different loads<br>CREATE WAREHOUSE FILE_WH_SMALL WAREHOUSE_SIZE = &#39;SMALL&#39; AUTO_SUSPEND = 60;<br>CREATE WAREHOUSE FILE_WH_LARGE WAREHOUSE_SIZE = &#39;LARGE&#39; AUTO_SUSPEND = 300;</pre><h4>Error Handling</h4><pre>-- Enhanced procedure with error handling<br>CREATE OR REPLACE PROCEDURE process_new_documents_safe()<br>RETURNS VARCHAR<br>LANGUAGE SQL<br>AS<br>$$<br>DECLARE<br>    error_count INT DEFAULT 0;<br>    success_count INT DEFAULT 0;<br>BEGIN<br>    FOR doc IN (SELECT relative_path FROM docs_inserted WHERE METADATA$ACTION = &#39;INSERT&#39;) DO<br>        BEGIN<br>            -- Process individual document<br>            INSERT INTO document_chunks (file_name, chunk_text)<br>            SELECT doc.relative_path, &#39;Processed: &#39; || doc.relative_path;<br>            SET success_count = success_count + 1;<br>        EXCEPTION<br>            WHEN OTHER THEN<br>                INSERT INTO processing_errors (file_name, error_message, error_time)<br>                VALUES (doc.relative_path, SQLERRM, CURRENT_TIMESTAMP());<br>                SET error_count = error_count + 1;<br>        END;<br>    END FOR;<br>    <br>    RETURN &#39;Success: &#39; || success_count || &#39;, Errors: &#39; || error_count;<br>END;<br>$$;</pre><h3>From Problem to Solution</h3><h4>What We Started With</h4><p>Remember our original problems:</p><ul><li><strong>1 hour 15 minutes</strong> from upload to searchable</li><li><strong>Processing 10,000 files</strong> when only 5 are new</li><li><strong>Complex batch failure recovery</strong></li><li><strong>High costs</strong> from redundant processing</li></ul><h4>What We Built</h4><p>A real-time file processing pipeline using Snowflake Streams + Cortex AI that:</p><ol><li><strong>Detects uploads instantly</strong> with Stream-based change tracking</li><li><strong>Processes only new files</strong> using METADATA$ACTION filtering</li><li><strong>Extracts text intelligently</strong> with Cortex PARSE_DOCUMENT</li><li><strong>Creates semantic search</strong> using vector embeddings</li><li><strong>Handles errors gracefully</strong> with automatic retry logic</li><li><strong>Scales automatically</strong> based on actual workload</li></ol><h4>What We Achieved</h4><p><strong>Before:</strong></p><ul><li>Upload PDF at 10:15:23 AM</li><li>Wait for batch job at 11:00 AM</li><li>Document searchable at 11:30 AM</li><li><strong>Total time: 1 hour 15 minutes</strong></li></ul><p><strong>After:</strong></p><ul><li>Upload PDF at 10:15:23 AM</li><li>Stream detects at 10:15:23 AM (instant)</li><li>AI processing at 10:15:25 AM (2 seconds later)</li><li>Searchable at 10:15:55 AM (<strong>30 seconds total</strong>)</li></ul><h3>The Real Business Impact</h3><p>For most organizations, moving from batch to real-time processing delivers:</p><p><strong>💰 Significant Time Savings</strong><br> Drastically reduces the delay between file upload and searchability</p><p><strong>📉 Lower Operational Costs</strong><br> Cuts down on unnecessary compute and AI processing</p><p><strong>⚡ Improved User Experience</strong><br> Enables instant access to newly uploaded documents</p><p><strong>🚀 Higher Developer Productivity</strong><br> Eliminates the need for complex batch job management and error handling</p><h3>Key Technical Innovations</h3><h4>Streams for Instant Detection</h4><pre>CREATE STREAM docs_inserted ON STAGE pdf_docs;<br>-- Zero polling, instant file upload detection</pre><h4>AI-Powered Processing</h4><pre>PARSE_DOCUMENT(&#39;@pdf_docs&#39;, file_path, {&#39;mode&#39;: &#39;LAYOUT&#39;})<br>-- Understands complex PDF layouts, tables, and structures</pre><h4>Smart Automation</h4><pre>CREATE TASK auto_process_docs<br>WHEN SYSTEM$STREAM_HAS_DATA(&#39;docs_inserted&#39;) <br>    OR SYSTEM$STREAM_HAS_DATA(&#39;docs_deleted&#39;)<br>-- Triggers for both file additions AND deletions</pre><h3>Why This Matters</h3><p>This isn’t just about faster file processing. It’s about fundamentally changing how we build data pipelines:</p><p>✅ <strong>Event-driven architecture</strong> instead of scheduled batches<br> ✅ <strong>AI-first processing</strong> that understands content semantically<br> ✅ <strong>Cost optimization</strong> through precise change detection<br> ✅ <strong>Real-time user experiences</strong> in data applications<br> ✅ <strong>Self-healing systems</strong> that handle failures automatically</p><p>The combination of Snowflake Streams for change detection and Cortex AI for intelligent processing creates a new paradigm for document workflows that’s both more efficient and more capable than traditional approaches.</p><h3>Try It Yourself</h3><p>Ready to transform your file processing from batch delays to real-time intelligence?</p><p>The complete code is available in this<a href="https://github.com/kameshsampath/snowflake-streams-ai-pipeline"> GitHub repository</a>. You can:</p><ol><li><strong>Clone the repo</strong> and run the setup scripts</li><li><strong>Upload your own PDFs</strong> to test the pipeline</li><li><strong>Customize the AI prompts</strong> for your document types</li><li><strong>Scale to production</strong> with the optimization patterns</li></ol><p>Start building with Snowflake Streams and Cortex AI today.</p><h3>Further Reading</h3><ul><li><a href="https://docs.snowflake.com/en/user-guide/streams-intro">Introduction to Streams in Snowflake</a></li><li><a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/aisql">Snowflake Cortex AI SQL Reference</a></li><li><a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-search/cortex-search-overview">Snowflake Search Service</a></li></ul><p><em>If you found this guide helpful, give it a clap 👏 and follow me for more practical guides on real-time data engineering and AI-driven automation.</em></p><p><strong>About the Author</strong><br>Kamesh Sampath is Lead Developer Advocate at Snowflake and a veteran technology evangelist with over 20 years of experience in cloud data platforms, AI, and developer productivity. He has helped organizations modernize their data and AI workflows using Snowflake, Kubernetes, and serverless technologies, and has been an active open source contributor for more than a decade.</p><p>Connect with Kamesh:<br>🔗 <a href="https://linkedin.com/in/kameshsampath">LinkedIn</a><br>💻 <a href="https://github.com/kameshsampath">GitHub</a><br>🐦 <a href="https://x.com/kamesh_sampath">Twitter</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=231f83edd22d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Secure and Streamline Snowflake Automation with Personal Access Tokens (PAT)]]></title>
            <link>https://kameshsampath.medium.com/secure-and-streamline-snowflake-automation-with-personal-access-tokens-pat-efa695183621?source=rss-b2156b61bbf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/efa695183621</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[snowflake]]></category>
            <category><![CDATA[programmatic-access-token]]></category>
            <dc:creator><![CDATA[Kamesh Sampath]]></dc:creator>
            <pubDate>Sat, 24 May 2025 03:43:07 GMT</pubDate>
            <atom:updated>2025-05-31T08:58:23.121Z</atom:updated>
            <content:encoded><![CDATA[<h3>Secure and Streamline Snowflake Automation with <a href="https://docs.snowflake.com/en/user-guide/programmatic-access-tokens">Programmatic</a> Access Tokens (PAT)</h3><p><a href="https://www.snowflake.com">Snowflake</a>’s <a href="https://docs.snowflake.com/en/user-guide/programmatic-access-tokens">Programmatic Access Tokens (PAT)</a> offer a modern, secure way to automate workflows and integrate with CI/CD pipelines — without relying on passwords or key pairs. In this post, you’ll learn how to enable PAT authentication in Snowflake, configure your environment, and use PATs with the <a href="https://docs.snowflake.com/en/developer-guide/snowflake-cli/authentication">Snowflake CLI</a> for seamless DevOps automation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VqYGqNVyTN3vHaZ4" /><figcaption>Photo by <a href="https://unsplash.com/@jakubpabis?utm_source=medium&amp;utm_medium=referral">Jakub Pabis</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Why Use Programmatic Access Tokens (PAT) in Snowflake?</h3><ul><li><strong>Security:</strong> PATs are scoped, time-limited, and can be revoked at any time.</li><li><strong>Automation:</strong> Ideal for CI/CD, GitHub Actions, and local scripts.</li><li><strong>Flexibility:</strong> Easily restrict PATs to specific roles and network policies.</li></ul><h3>Prerequisites</h3><ul><li>A <a href="https://signup.snowflake.com/">Snowflake Trial Account</a> (if you don’t have one)</li><li>A Snowflake user with ACCOUNTADMIN or equivalent privileges</li><li><a href="https://docs.snowflake.com/en/developer-guide/snowflake-cli/index">Snowflake CLI</a> (v3.9.0 or higher)</li><li><a href="https://stedolan.github.io/jq/">jq</a> (for JSON parsing)</li><li>Github CLI ( for interacting with Github)</li></ul><h3>Sources</h3><p>The complete demo sources is available at <a href="https://github.com/kameshsampath/snowflake-pat-demo">https://github.com/kameshsampath/snowflake-pat-demo</a></p><h3>Step 1: Enable PAT Authentication Policy in Snowflake</h3><p>First, ensure your Snowflake environment supports PATs and set up an authentication policy with the desired settings. Here’s an example SQL snippet:</p><p>Set the following variables:</p><pre># your Snowflake account user<br>export SNOWFLAKE_USER=&quot;&lt;your snowflake account user&gt;&quot;<br># this we will update once we have the PAT and setup local connection<br>export SNOWFLAKE_DEFAULT_CONNECTION_NAME=&quot;&lt; your snowflake connection name&gt;&quot;<br>export MY_PAT_DEMO_ROLE=&quot;&lt; the role to use to restrict PAT&gt;&quot;<br># if your are using KeyPair Auth with Snowflake then set if your key is encrypted<br># export PRIVATE_KEY_PASSPHRASE=&quot;your private keypassword&quot;    </pre><p>Let us prepare the Snowflake environment for the Demo</p><pre>#!/usr/bin/env sh<br><br>set -euo pipefail<br><br>snow sql --stdin &lt;&lt;EOF<br>-- create demo database to hold all our demo objects<br>create or replace database my_demo_db;<br>use database my_demo_db;<br>-- schema to hold all the policies<br>create or replace schema policies;<br>-- schema to hold all network rules<br>create or replace schema networks;<br>-- Create or alter the authentication policy<br>create or alter authentication policy my_demo_db.policies.demos_auth_policy<br>  authentication_methods = (&#39;PASSWORD&#39;, &#39;OAUTH&#39;, &#39;KEYPAIR&#39;, &#39;PROGRAMMATIC_ACCESS_TOKEN&#39;)<br>  pat_policy = (<br>    default_expiry_in_days=7,<br>    max_expiry_in_days=90,<br>    network_policy_evaluation = ENFORCED_NOT_REQUIRED<br>  );<br>EOF</pre><p><strong>What does this do?</strong></p><ul><li>Allows multiple authentication methods, including PATs.</li><li>Sets PAT default expiry to 7 days, max to 90 days.</li><li>Enforces network policy evaluation for added security.</li></ul><p>Run the following script to create those resources:</p><pre>curl -Ls \<br>  https://raw.githubusercontent.com/kameshsampath/snowflake-pat-demo/refs/heads/main/prepare.sh \<br>  | sh</pre><h3>Step 2: Generate a Personal Access Token (PAT)</h3><p>You can create a PAT for your Snowflake user and restrict it to a specific role. Here’s how to do it using the Snowflake CLI and jq:</p><pre>export SNOWFLAKE_PASSWORD=$(snow sql \<br>  --query &quot;ALTER USER IF EXISTS $SNOWFLAKE_USER ADD PAT my_demo_pat \<br>   ROLE_RESTRICTION = $MY_PAT_DEMO_ROLE&quot; --format=json \<br>   | jq -r &#39;.[] | .token_secret&#39;)</pre><ul><li>This command creates a PAT named my_demo_pat for your user, restricted to the $MY_PAT_DEMO_ROLE.</li><li>The token is stored in the SNOWFLAKE_PASSWORD environment variable for use with the CLI.</li></ul><p>Verify if the PAT has been created successfully</p><pre>snow sql -q &quot;show pats for user $SNOWFLAKE_USER&quot;</pre><h3>Step 3: Configure the Snowflake CLI to Use PAT</h3><p>To ensure our PAT works let us reconfigure the Snowflake CLI with PAT. To ensure we dont corrupt existing configuration we will setup a new Snowflake configuration home that the CLI can use.</p><ol><li><strong>Test your connection and export details:</strong></li></ol><pre>snow connection test --format=json &gt; &quot;$PWD/connection.json&quot;<br>export SNOWFLAKE_ACCOUNT=$(jq -r &#39;.Account&#39; &quot;$PWD/connection.json&quot;)<br>export SNOWFLAKE_USER=$(jq -r &#39;.User&#39; &quot;$PWD/connection.json&quot;)<br>export SNOWFLAKE_HOST=$(jq -r &#39;.Host&#39; &quot;$PWD/connection.json&quot;)</pre><p><strong>2. Verify PAT using Temporary Connection:</strong></p><pre>snow sql --temporary-connection --account=&quot;${SNOWFLAKE_ACCOUNT}&quot; \<br>  --host=&quot;${SNOWFLAKE_HOST}&quot; \<br>  --user=&quot;${SNOWFLAKE_USER}&quot; \<br>  --query &quot;select current_user(), current_role(), current_database(), current_schema()&quot;</pre><blockquote>The connection fails with an error “Invalid connection configuration. 250001 (08001): None: Failed to connect to DB: &lt;your host&gt;:443. Fail : Network policy is required.” It indicates that the Snowflake user used as part of the connection does not have any “network_policy”associated.</blockquote><p>4.<strong> Create and Set Network Access Policy on User</strong></p><p>In the following example, we create a network policy that allows access from your local IP address only.</p><p>Let us first build CIDRs list, for an example we will use GitHub Actions CIDRs combined with the your local machine public IP .</p><pre>#!/usr/bin/env sh<br><br>set -euo pipefail <br><br># Get GitHub Actions IP ranges only IPV4<br>GH_CIDRS=$(curl -s https://api.github.com/meta | jq -r &#39;.actions | map(select(contains(&quot;:&quot;) | not)) | map(&quot;&#39;\&#39;&#39;&quot; + . + &quot;&#39;\&#39;&#39;&quot;) | join(&quot;,&quot;)&#39;)<br><br># Get local IP and add /32 suffix<br>LOCAL_IP=&quot;$(dig +short myip.opendns.com @resolver1.opendns.com)/32&quot;<br><br># Combine GitHub CIDRs and local IP<br>CIDR_VALUE_LIST=&quot;${GH_CIDRS},&#39;${LOCAL_IP}&#39;&quot;<br><br>snow sql --stdin &lt;&lt;EOF<br>use role $MY_PAT_DEMO_ROLE;<br>-- create a new network rule that allows all CIDR (for demo only)<br>create network rule if not exists my_demo_db.networks.pat_gh_actions_local_access_rule<br>mode = ingress<br>type = ipv4<br>value_list = ($CIDR_VALUE_LIST)<br>comment = &#39;Allow only GitHub Actions and local machine IPv4 addresses&#39;;<br><br>use role accountadmin;<br>-- attach the network rule to the network policy<br>create network policy if not exists GH_ACTIONS_PAT_NETWORK_POLICY<br>allowed_network_rule_list = (&#39;my_demo_db.networks.at_gh_actions_local_access_rule&#39;)<br>comment = &#39;Network policy to allow all IPv4 addresses.&#39;;<br><br>-- set the network policy to the user to allow using pat with snowflake cli<br>alter user $SNOWFLAKE_USER set network_policy=&#39;GH_ACTIONS_PAT_NETWORK_POLICY&#39;;<br>EOF</pre><blockquote>A special note of thanks to <a href="https://medium.com/u/2d442c0dbd49">Myles Borins</a> for the tip on getting GitHub Actions CIDRs</blockquote><p>Execute the script</p><pre><br>curl -Ls <br>   https://raw.githubusercontent.com/kameshsampath/snowflake-pat-demo/refs/heads/main/create_network_rule.sh \<br>   | sh</pre><p>Now running the same connection test we did earlier, will succeed.</p><pre>snow sql --temporary-connection --account=&quot;${SNOWFLAKE_ACCOUNT}&quot; \<br>  --host=&quot;${SNOWFLAKE_HOST}&quot; \<br>  --user=&quot;${SNOWFLAKE_USER}&quot; \<br>  --query &quot;select current_user(), current_role(), current_database(), current_schema()&quot;</pre><h3>GitHub Actions Integration</h3><p>Fork and clone repository to your local machine.</p><pre>gh repo fork --clone https://github.com/kameshsampath/snowflake-pat-demo.git<br>cd snowflake-pat-demo</pre><p>Then, set up the GitHub secrets for your repository:</p><pre>gh secret set SNOWFLAKE_PASSWORD --body &quot;$SNOWFLAKE_PASSWORD&quot;<br>gh secret set SNOWFLAKE_ACCOUNT --body &quot;$SNOWFLAKE_ACCOUNT&quot;<br>gh secret set SNOWFLAKE_USER --body &quot;$SNOWFLAKE_USER&quot;<br>gh secret set SNOWFLAKE_HOST --body &quot;$SNOWFLAKE_HOST&quot;</pre><p>Now just do a empty commit and push to trigger the GitHub Actions workflow.</p><pre>git commit - allow-empty -m &quot;Trigger GitHub Actions&quot;<br>git push</pre><p>This will run the workflow defined in `.github/workflows/snowflake-pat-demo.yml`, which will use the PAT to connect to Snowflake and perform operations</p><h3>Cleanup</h3><p>If you want to clean up all the resources that were created during this demo,</p><pre>#!/usr/bin/env sh<br>snow sql --stdin &lt;&lt;EOF<br>use role accountadmin;<br>alter user ${SNOWFLAKE_USER} unset network_policy;<br>alter user ${SNOWFLAKE_USER} remove pat my_demo_pat;<br><br>drop network policy if exists GH_ACTIONS_PAT_NETWORK_POLICY;<br>use role $MY_PAT_DEMO_ROLE;<br>drop database if exists my_demo_db;<br>EOF<br><br>gh secret delete SNOWFLAKE_PASSWORD<br>gh secret delete SNOWFLAKE_ACCOUNT<br>gh secret delete SNOWFLAKE_USER<br>gh secret delete SNOWFLAKE_HOST<br><br>rm -rf &quot;$PWD/connection.json&quot;</pre><p>Run the script</p><pre>curl -Ls \<br>  https://raw.githubusercontent.com/kameshsampath/snowflake-pat-demo/refs/heads/main/cleanup.sh \<br>  | sh</pre><h3>Security Best Practices</h3><h4>Authentication</h4><ul><li>Use role restrictions with PATs to limit scope</li><li>Set appropriate expiration periods (recommended: 7–30 days)</li><li>Rotate PATs regularly and before expiration</li></ul><h4>Network Security</h4><ul><li>Always use HTTPS for API calls</li><li>Restrict network access to specific IP ranges in production</li><li>Regularly audit and update allowed IP ranges</li></ul><h4>Credential Management</h4><ul><li>Store PATs securely using secret managers</li><li>Never commit PATs to version control</li><li>Use separate PATs for different applications/services</li></ul><h3>Conclusion</h3><p>Programmatic Access Tokens make it easy and secure to automate Snowflake workflows, especially in DevOps and CI/CD scenarios. By following the steps above, you can enable, generate, and use PATs with the Snowflake CLI — unlocking powerful automation for your data projects.</p><p><em>Happy automating with Snowflake!</em></p><h3>Further Reading</h3><ul><li><a href="https://docs.snowflake.com/en/user-guide/programmatic-access-tokens">Snowflake: Personal Access Tokens (PAT)</a></li><li><a href="https://docs.snowflake.com/en/developer-guide/snowflake-cli/index">Snowflake CLI Authentication Methods</a></li><li><a href="https://docs.snowflake.com/en/sql-reference/sql/create-authentication-policy">Snowflake: CREATE AUTHENTICATION POLICY</a></li><li><a href="https://docs.snowflake.com/en/user-guide/security-access-control-overview">Snowflake: Managing Users and Roles</a></li><li><a href="https://docs.snowflake.com/en/user-guide/network-policies">Snowflake: Network Policies</a></li><li><a href="https://github.com/snowflakedb/snowsql">Snowflake CLI GitHub Repository</a></li><li><a href="https://stedolan.github.io/jq/">jq: Command-line JSON processor</a></li><li><a href="https://direnv.net/">direnv: Environment Variable Management</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=efa695183621" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>