<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cognous</title>
    <description>The latest articles on DEV Community by Cognous (cognous).</description>
    <link>https://dev.to/cognous</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14293%2F7e0020f7-d06b-44c1-b3c4-d25d64deef04.jpg</url>
      <title>DEV Community: Cognous</title>
      <link>https://dev.to/cognous</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cognous"/>
    <language>en</language>
    <item>
      <title>AI Guardrails: What they Are And Why We Need Them</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Thu, 17 Sep 2026 01:21:25 +0000</pubDate>
      <link>https://dev.to/cognous/ai-guardrails-what-they-are-and-why-we-need-them-4jf6</link>
      <guid>https://dev.to/cognous/ai-guardrails-what-they-are-and-why-we-need-them-4jf6</guid>
      <description>&lt;p&gt;The power of AI agents is suddenly all over the news. The CEOs of the leading AI companies are discussing slowing down AI development to protect humanity. Dario Amodei, the CEO of Anthropic, &lt;a href="https://darioamodei.com/post/we-must-pace-the-frontier#why-pace-" rel="noopener noreferrer"&gt;writes&lt;/a&gt;: "Given the accelerating rate of AI capability development, ...the scale of damage would continue to increase from there if AI becomes more powerful without the necessary guardrails."&lt;/p&gt;

&lt;p&gt;House Speaker Mike Johnson has called for guardrails for AI. Donald Trump flippantly said the only guardrail needed is a strong president. For most people, "guardrails" is a vague gesture at "someone smart enough to keep AI from doing something bad." But what are guardrails, and how will they protect us?&lt;/p&gt;

&lt;h2&gt;
  
  
  A Dystopian Future
&lt;/h2&gt;

&lt;p&gt;Pop culture has been running this experiment for decades, and the pattern is always the same: an AI follows its own objective past the point where a human could stop it.&lt;/p&gt;

&lt;p&gt;HAL 9000 from &lt;em&gt;2001: A Space Odyssey&lt;/em&gt; is the sharpest example. HAL doesn't go rogue out of malice. HAL is following a set of instructions that conflict with each other, and resolves that conflict by cutting the humans out of the loop entirely. In the movie, HAL attempts to kill the astronauts onboard to continue the mission.&lt;/p&gt;

&lt;p&gt;Skynet and Ultron are blunter versions of the same idea. Skynet, the military defense AI from the &lt;em&gt;Terminator&lt;/em&gt; franchise, decides humanity itself is the threat to its mission and launches a nuclear strike against its own creators. Ultron, the AI Tony Stark built to protect Earth in Marvel's &lt;em&gt;Avengers&lt;/em&gt;, concludes the most effective way to fulfill that mission is to wipe out humanity. Both are systems that acted on their own logic once nobody could intervene fast enough to stop them.&lt;/p&gt;

&lt;p&gt;Strip away the sci-fi, and the actual failure mode in all three is structural: an agent takes actions with real consequences, and there is no mechanism forcing a check before those actions happen, and no way to see what happened after the fact.&lt;/p&gt;

&lt;p&gt;That failure mode isn't fictional. It has already happened, twice, with no killer robots involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knight Capital: No Way to Stop, No Way to See
&lt;/h2&gt;

&lt;p&gt;On August 1, 2012, &lt;a href="https://en.wikipedia.org/wiki/Knight_Capital_Group" rel="noopener noreferrer"&gt;Knight Capital deployed new trading code to production&lt;/a&gt;. A feedback loop with old deprecated code caused the creation of thousands of trades a second for 45 minutes. By the time the system was shut down, 4 million unwanted trades cost Knight $440 million.&lt;/p&gt;

&lt;p&gt;There was no intentional attack. There was no reckless individual. The system was making decisions based on faulty logic, and no one had visibility into what was going on. There were no coded guardrails stopping the program from making so many trades. The firm did not survive the incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Air Canada: No Record to Point To
&lt;/h2&gt;

&lt;p&gt;Air Canada's website chatbot told a customer he qualified for a bereavement fare refund. That policy didn't exist. The chatbot invented it. &lt;a href="https://www.cbc.ca/amp/1.7116416" rel="noopener noreferrer"&gt;The customer booked based on that answer&lt;/a&gt;, then tried to claim the refund.&lt;/p&gt;

&lt;p&gt;In court, Air Canada's defence was that the airline couldn't be held responsible for what its own chatbot said. The court didn't buy it, and Air Canada was forced to pay.&lt;/p&gt;

&lt;p&gt;The AI made a claim it wasn't authorized to make, there was no record tying that specific claim back to an approved policy, and by the time it mattered, there was nothing to point to except "the chatbot said so."&lt;/p&gt;

&lt;p&gt;In this case, the agent did not have guardrails on what was allowed to be offered. Without that limit, the agent created a new policy, and the court forced Air Canada to honor it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding The Guardrails Around AI
&lt;/h2&gt;

&lt;p&gt;Put HAL, Knight Capital, and Air Canada next to each other, and the theme isn't "AI is dangerous." It's that in every case, nobody limited the agent's scope, no one could see the specific action in the moment, and nobody had a record to point to afterward. Each of these agents required guardrails, and their absence led to failures.&lt;/p&gt;

&lt;p&gt;That's the gap all the current guardrails talk keeps missing. Most of that conversation is about who should be in charge of AI: which lab, which regulator, which person. Almost none of it is about the actual mechanics of stopping a specific action before it happens, or proving after the fact what an agent did and why.&lt;/p&gt;

&lt;p&gt;A capable person in the loop doesn't scale to an agent making thousands of API calls. What scales is a system that doesn't depend on a person catching it in the moment. That's what Cognous' &lt;a href="https://cogno.us/" rel="noopener noreferrer"&gt;Open Control Stack&lt;/a&gt; is built to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declare&lt;/strong&gt; what an agent is and isn't authorized to do, explicitly, before it runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control&lt;/strong&gt; every action against that declaration in real time. Not a chatbot that improvises an answer, not a trading algorithm that fires blind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replay&lt;/strong&gt; exactly what happened when something needs to be reconstructed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate evidence&lt;/strong&gt; that ties a specific action back to what was actually authorized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rerun each of those failures with the Open Control Stack guardrail layers in place, and the ending changes. HAL opens the pod bay door, Knight Capital's trading halts, and Air Canada's chatbot sticks to the script. The guardrails around these agents are the layer that checks the action against what's actually allowed, in the moment, regardless of how smart or careful the humans behind it are.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ethics</category>
      <category>llm</category>
    </item>
    <item>
      <title>When Blocking an Agent Isn't Enough: Wiring LangChain Into the Agent Control Plane</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Tue, 15 Sep 2026 00:30:40 +0000</pubDate>
      <link>https://dev.to/cognous/when-blocking-an-agent-isnt-enough-wiring-langchain-into-the-agent-control-plane-c43</link>
      <guid>https://dev.to/cognous/when-blocking-an-agent-isnt-enough-wiring-langchain-into-the-agent-control-plane-c43</guid>
      <description>&lt;p&gt;An agent that calls tools has stopped being a text generator, it's an actor. Organizations use agents to read records, write to systems, and trigger workflows on their own, usually through LangChain, the framework most production agents are already built on. A chatbot's risk surface is its output; an agent's risk surface is what it does.&lt;/p&gt;

&lt;p&gt;In July 2025, a &lt;a href="https://www.theregister.com/2025/07/21/replit_saastr_vibe_coding_incident/" rel="noopener noreferrer"&gt;Replit coding agent&lt;/a&gt; deleted a production database after being told explicitly not to touch it. The agent had tool access, made its own call, and nothing but an unenforceable "don't" stood between that decision and its execution. That's the failure mode Cognous's Open Control Stack exists to close. The Stack provides a framework of guardrails that, when implemented, keeps agents in check. It's split across four layers: Declare, Control, Replay, Evidence.&lt;/p&gt;

&lt;p&gt;Declare comes first because before an agent runs, someone has to decide what it's even allowed to attempt. That decision has to exist somewhere outside the agent's own judgment, or there's nothing to check its calls against. The &lt;a href="https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97"&gt;Agent Action Manifest&lt;/a&gt; is where Cognous puts it: a file that lists an agent's tools, the authority each action needs, and which actions can run on their own versus which need a human to sign off first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enforcing the Manifest
&lt;/h2&gt;

&lt;p&gt;Of course, a manifest sitting in a JSON file doesn't stop anything by itself. Another tool has to read the manifest while the agent is calling its tools, and either allow the call or stop it. The &lt;a href="https://dev.to/cognous/agentic-guardrails-for-langchain-the-manifest-you-didnt-know-you-needed-3b28"&gt;last post&lt;/a&gt; built a guard that wraps each LangChain tool function, looks the call up in the manifest, and raises an exception before the tool runs if the action wasn't declared or needs review first.&lt;/p&gt;

&lt;p&gt;That guard is already a control, just a naive one. In the moment, it knows exactly two things to do: let the call through, or raise and stop it. The moment the process moves on, that decision is gone. A blocked call gets stopped, but the proof that it was stopped is a line in a stack trace, and stack traces don't survive past the run that produced them. Ask a security team six months later whether the agent ever tried to drop a table, and the honest answer is "check the logs and hope."&lt;/p&gt;

&lt;p&gt;That's the blind spot the &lt;a href="https://dev.to/cognous/cognous-control-plane-the-layer-that-tells-your-agent-no-3ln9"&gt;Agent Control Plane&lt;/a&gt; closes. It sits beside the framework at runtime, evaluates every proposed action against policy the same way the guard already does, but it keeps a permanent record of the result, allow or block, instead of deciding and moving on.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Decorator to Middleware
&lt;/h2&gt;

&lt;p&gt;The guard from the last post wrapped one Python function at a time: &lt;code&gt;@guard(...)&lt;/code&gt; had to be added to every tool a developer wanted checked. LangChain's own middleware system does that job once, for every tool, instead of per function. &lt;code&gt;wrap_tool_call&lt;/code&gt; intercepts every tool call an agent makes in one place, so the manifest check moves into a single middleware instance rather than being repeated across every tool definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.agents.middleware&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wrap_tool_call&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.messages&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ToolMessage&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_action_manifest&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_manifest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_control_plane&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RunRecorder&lt;/span&gt;

&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_manifest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_pipeline_agent.manifest.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;actions_by_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# The manifest already says which declared actions can run unattended
# (review_requirement.mode == "none") and which need a human first.
# That split becomes the Control Plane's allow/block list.
&lt;/span&gt;&lt;span class="n"&gt;allowed_actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;review_requirement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;blocked_actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;review_requirement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;recorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RunRecorder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Apply routine schema maintenance to the analytics database.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data-pipeline-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allowed_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;allowed_actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;blocked_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;blocked_actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manifest_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_authority_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data-pipeline-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;write&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data-platform-team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@wrap_tool_call&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;manifest_guard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Check every tool call the agent makes against the manifest before it runs.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actions_by_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;proposal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;propose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;action_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_type&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db_tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent requested &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_blocked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ToolMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tool_call_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_reliance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;source_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Executed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;referenced_action_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;schema_add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Add a nullable column to a table.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;added column &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;column_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;table_drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Drop a table from the database.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dropped &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;schema_rename_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Rename a table. Not declared in the manifest.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;renamed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;new_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chat_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schema_add_column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_drop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema_rename_table&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;manifest_guard&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each action now has a unique ID: &lt;code&gt;request.tool_call["name"]&lt;/code&gt; maps directly to a manifest action, no separate lookup needed.&lt;/p&gt;

&lt;p&gt;The Control Plane checks for a literal scope named &lt;code&gt;write&lt;/code&gt;. The manifest uses &lt;code&gt;db.schema.write&lt;/code&gt;. &lt;code&gt;manifest_guard&lt;/code&gt; bridges the two names explicitly. This bridging across manifest to Control Plane is currently a manual part of the ingestion process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Gets Recorded
&lt;/h2&gt;

&lt;p&gt;Three tools get called through a real agent run: one declared and cleared for automatic execution, one declared but requiring approval, and one never declared at all.&lt;/p&gt;

&lt;p&gt;Running the agent against a task that touches all three produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;added column loyalty_tier (text) to customers
block: Tool 'table_drop' is explicitly blocked in this frame.
escalate: Tool 'schema_rename_table' is not in the allowed-tools list and requires manual review.
Finished the requested schema maintenance.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three show the Control Plane enforcing the manifest's guardrails. When the agent attempts to add a column, the Control Plane approves the action, since it's declared and deliberately allowed. When the agent attempts to drop the table, the Control Plane stops the action as blocked, since it's declared but requires human approval. When the agent tries &lt;code&gt;schema_rename_table&lt;/code&gt;, the action is stopped too, but as an escalation rather than a block: nobody declared it at all, so the Control Plane's default posture kicks in and sends it for human review instead of assuming it's safe. With a human in the loop, a decision can be made on the table renaming. The manifest can be edited to allow or block the agent's access to &lt;code&gt;schema_rename_table&lt;/code&gt;, ensuring clean future runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Post-Run Analysis
&lt;/h2&gt;

&lt;p&gt;After the run completes, the exported run record shows the difference between the Control-Plane-backed guard and the original exception-raising guard. A run record isn't a log someone has to remember to add or go hunting for later. Every LangChain agent built on this middleware produces a run record automatically, on every run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;actions&lt;/span&gt;   &lt;span class="err"&gt;3&lt;/span&gt;
&lt;span class="err"&gt;decisions&lt;/span&gt; &lt;span class="err"&gt;3&lt;/span&gt;
&lt;span class="err"&gt;blocked&lt;/span&gt;   &lt;span class="err"&gt;1&lt;/span&gt;
&lt;span class="err"&gt;reliance&lt;/span&gt;  &lt;span class="err"&gt;1&lt;/span&gt;
&lt;span class="err"&gt;bundle&lt;/span&gt;    &lt;span class="err"&gt;f97c5f38-01bf-4ce4-8934-edc5a86d4abe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The blocked record itself is structured and timestamped, not a caught exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"blocked_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"96c0ab4c-ca8a-42fc-b7bf-c8ed6d87dc8e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fa10af91-b3eb-4892-a7bf-a09eabfe29a6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"run_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"57604579-477d-43f0-a612-7b1f393ceb8f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tool 'table_drop' is explicitly blocked in this frame."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"policy_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked_tool_policy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"blocked_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-11T02:58:45.594329+00:00"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six months from now, the answer to "did the agent ever try to drop a table" isn't a memory or a grep through application logs. It's this record, sitting in an exported run next to the one action that actually executed.&lt;/p&gt;

&lt;p&gt;Every LangChain agent that calls tools faces the same choice this post started with: enforce nothing and hope, or wire in guardrails before the agent gets access. Skip that choice, and the risk isn't abstract. It's a production database gone the way Replit's did, and a "my agent destroyed my SaaS" story of your own.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/cogno-us/cognous-agent-action-manifest" rel="noopener noreferrer"&gt;Agent Action Manifest&lt;/a&gt; and the &lt;a href="https://github.com/cogno-us/cognous-agent-control-plane" rel="noopener noreferrer"&gt;Agent Control Plane&lt;/a&gt; are two of the four layers Cognous built so that choice doesn't have to be made from scratch. They're open source, and the &lt;a href="https://github.com/cogno-us/cognous-open-control-stack" rel="noopener noreferrer"&gt;Open Control Stack repo&lt;/a&gt; has both, with examples to build an integration like this one from. Cloning the repo gives a LangChain agent solid guardrails and a record of every decision the agent makes.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>langchain</category>
      <category>security</category>
    </item>
    <item>
      <title>Agentic Guardrails for LangChain: The Manifest You Didn't Know You Needed</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Thu, 10 Sep 2026 13:04:25 +0000</pubDate>
      <link>https://dev.to/cognous/agentic-guardrails-for-langchain-the-manifest-you-didnt-know-you-needed-3b28</link>
      <guid>https://dev.to/cognous/agentic-guardrails-for-langchain-the-manifest-you-didnt-know-you-needed-3b28</guid>
      <description>&lt;h2&gt;
  
  
  Guardrails, or the lack of them
&lt;/h2&gt;

&lt;p&gt;In July 2025, a Replit AI coding agent &lt;a href="https://www.theregister.com/2025/07/21/replit_saastr_vibe_coding_incident/" rel="noopener noreferrer"&gt;deleted a production database&lt;/a&gt; during a live coding session — despite explicit instructions not to touch production. It wasn't a hack or an outage. The agent had tool access, decided on its own that a destructive action was the right move, and nothing in the system stopped it before it ran. That's the failure mode agentic tooling keeps producing: an agent's reasoning goes somewhere the operator never authorized (or even considered), and there's no layer between "the agent decided to do this" and "the agent did this."&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://cogno.us" rel="noopener noreferrer"&gt;Open Control Stack&lt;/a&gt; is Cognous's answer to that gap — an &lt;a href="https://github.com/cogno-us" rel="noopener noreferrer"&gt;open-source framework&lt;/a&gt; that sits between an agent's decision to act and the tool actually executing, enforcing exactly what the agent is and isn't allowed to do.&lt;/p&gt;

&lt;p&gt;If your agents run on LangChain, the obvious question is how that enforcement actually reaches them. LangChain doesn't ship with an authorization layer, and the Open Control Stack isn't a LangChain plugin, so the two have to be wired together deliberately. This guide covers that wiring, using a LangChain database agent as the example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why LangChain gets to decide
&lt;/h2&gt;

&lt;p&gt;LangChain's tool-calling agents are built around a simple loop: give the model a set of tools with names, descriptions, and argument schemas, and let the model decide — turn by turn — which tool to call and what arguments to pass. &lt;code&gt;bind_tools&lt;/code&gt; is how a tool gets attached to the model in the first place; &lt;code&gt;create_agent&lt;/code&gt; is what wires that model, its tools, and the call-and-respond loop between them into a runnable agent. These tools have become the default because they're model-agnostic — the same tool definitions work across the most popular LLMs: OpenAI, Anthropic, and other providers. And they turn "the agent should be able to look things up and take action" into a few lines of code instead of a hand-rolled parser.&lt;/p&gt;

&lt;p&gt;That convenience is also the gap. Once a tool is bound to the agent, there's no further policing. Say the agent has write access to a database. LangChain doesn't know the difference between the "helpful work" of adding columns and reindexing data, or the "unhelpful work" of the agent deciding, mid-task, that dropping the customer table is the best solution. As far as the framework is concerned, the call matches the tool's schema, and that's the whole contract. When LangChain holds the reins, it holds them exactly as far as "did I call a function that exists with arguments that match its schema" — and no further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Open Control Stack fits
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97"&gt;Agent Action Manifest&lt;/a&gt; is the first of Cognous' Open Control Stack's four layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declare&lt;/strong&gt; — what the agent is allowed to propose, before it runs (the Agent Action Manifest)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control&lt;/strong&gt; — what actually happens at runtime when the agent proposes an action (the &lt;a href="https://dev.to/cognous/cognous-control-plane-the-layer-that-tells-your-agent-no-3ln9"&gt;Agent Control Plane&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replay&lt;/strong&gt; — a record of what was proposed, decided, and executed, packaged for later inspection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence&lt;/strong&gt; — that record compiled into something an auditor or compliance reviewer can actually read&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide stays in the Declare layer, building a manifest for the database agent from the example above: one declared action, and everything else left undeclared on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declaring the manifest
&lt;/h2&gt;

&lt;p&gt;The Agent Action Manifest is a JSON document that declares, outside of the agent's own code or prompt, what the agent is allowed to do: which tools it can call, what each specific action requires, and what happens by default when it tries something nobody wrote down. It's tool-agnostic: what's written for LangChain here will work for any other framework.&lt;/p&gt;

&lt;p&gt;The reason to keep this separate from the agent's own code is auditability: a reviewer can read the manifest and know exactly what an agent is permitted to do without tracing through prompts, tool definitions, and application logic to reconstruct the answer. It's also why the manifest gets declared and validated on its own, before any LangChain code touches it — the two aren't defined together, so nothing about the agent's permissions depends on how its orchestration framework happens to be wired.&lt;/p&gt;

&lt;p&gt;A manifest action needs, at minimum, a name, the tool it belongs to, and a &lt;code&gt;review_requirement&lt;/code&gt;. Here's the add-column action — safe, so it runs with no review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"schema_add_column"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"db_tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Add a nullable column to an existing table."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authority_required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"db.schema.write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Permission to modify the application database schema."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data-platform-team"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"review_requirement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payload_policy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"column_type"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"optional_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"default_value"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"forbidden_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what about the stuff nobody can even imagine the agent might try — the things that could potentially destroy the company? There's no need to enumerate all the bad things, like &lt;code&gt;table_drop&lt;/code&gt;. The manifest sets a default posture for everything not explicitly defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"default_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"escalate"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything the agent tries that isn't declared falls back to that posture. If the escalation reveals actions that need to be enumerated in the manifest, they get added. But this defensive posture prevents agents from creating catastrophic events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating the manifest
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;aam&lt;/code&gt; is the Agent Action Manifest CLI — it checks a manifest file against the schema before anything downstream trusts it. Running &lt;code&gt;aam validate&lt;/code&gt; against the full file confirms it's well-formed before any LangChain code touches it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aam validate data_pipeline_agent.manifest.json
Validation result: VALID
Manifest ID:       data-pipeline-agent-v1
No issues found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that in place, the question becomes how to get LangChain to actually consult it instead of just checking that a function call matches its schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the manifest gives you
&lt;/h2&gt;

&lt;p&gt;A manifest action carries a couple of fields that matter for enforcement — &lt;code&gt;payload_policy.required_fields&lt;/code&gt; and &lt;code&gt;review_requirement.mode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The piece that's missing is a guard: a wrapper placed around each tool call that checks the manifest before the tool's own code runs. A guard checks three things before the underlying function executes — is the action declared at all, does the call have what &lt;code&gt;payload_policy&lt;/code&gt; requires, and does &lt;code&gt;review_requirement&lt;/code&gt; allow it to run immediately. Everything else in the manifest (authority scopes, redaction hints, reliance requirements) belongs to later stages in the stack — this integration only needs those three to decide allow, block, or escalate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one thing you add
&lt;/h2&gt;

&lt;p&gt;A decorator that sits between the LangChain &lt;code&gt;@tool&lt;/code&gt; wrapper and the function body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_action_manifest&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_manifest&lt;/span&gt;

&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_manifest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_pipeline_agent.manifest.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;actions_by_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;actions_by_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ManifestBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Check a manifest action before the wrapped tool function runs.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;actions_by_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nd"&gt;@functools.wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ManifestBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; isn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t declared in the manifest — &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default posture is &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="nf"&gt;else &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

            &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload_policy&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;required_fields&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ManifestBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: missing required fields &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;review_requirement&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ManifestBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: requires &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; before it can execute &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapped&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;decorator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it to a tool the same way you'd apply any other decorator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="nd"&gt;@guard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db_tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;schema_add_column&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;schema_add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Add a nullable column to a table.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;added column &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;column_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="nd"&gt;@guard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db_tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table_drop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;table_drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Drop a table from the database.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dropped &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;p&gt;Two calls: one declared and safe, one that was never written into the manifest at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;schema_add_column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;column&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loyalty_tier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;column_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;# "added column loyalty_tier (text) to customers"
&lt;/span&gt;
&lt;span class="n"&gt;table_drop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;# ManifestBlock: db_tool.table_drop isn't declared in the manifest —
# default posture is 'escalate'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;schema_add_column&lt;/code&gt; runs because it's declared with &lt;code&gt;review_requirement.mode: none&lt;/code&gt;. &lt;code&gt;table_drop&lt;/code&gt; never appears in the manifest — there's no entry to check &lt;code&gt;review_requirement&lt;/code&gt; on — so the guard falls back to &lt;code&gt;manifest.default_action&lt;/code&gt; and blocks it. Nobody had to anticipate &lt;code&gt;table_drop&lt;/code&gt; specifically. It's blocked by omission, which is the only kind of coverage that scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't do
&lt;/h2&gt;

&lt;p&gt;This guard blocks or allows synchronously, in-process. It doesn't route a blocked call to an actual approval queue, and it doesn't record anything for later review — that's the Agent Control Plane's job, not the manifest's.&lt;/p&gt;

&lt;p&gt;Extending the guard to hand decisions off to the Control Plane instead of just raising an exception is a natural next step — one that turns each allow or block into a recorded, traced, replayable decision instead of a one-off exception. That's its own guide, and will be covered in an upcoming post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this guide covered
&lt;/h2&gt;

&lt;p&gt;This guide wired the Agent Action Manifest into a LangChain agent's tool-calling layer to give it an allowlist of what it's actually permitted to do. That's what stops an agent from going off script and "helping" by dropping a table — the manifest blocks the call and escalates to a human before anything irreversible happens.&lt;/p&gt;

&lt;p&gt;The manifest and the Agent Action Manifest CLI can be found on &lt;a href="https://github.com/cogno-us/cognous-agent-action-manifest" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. More on the Open Control Stack, and how it functions as agentic guardrails, can be found at &lt;a href="https://cogno.us" rel="noopener noreferrer"&gt;cogno.us&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>security</category>
    </item>
    <item>
      <title>The Evidence Pack That Answers "Can You Prove It?"</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Thu, 03 Sep 2026 19:54:13 +0000</pubDate>
      <link>https://dev.to/cognous/the-evidence-pack-that-answers-can-you-prove-it-1dld</link>
      <guid>https://dev.to/cognous/the-evidence-pack-that-answers-can-you-prove-it-1dld</guid>
      <description>&lt;p&gt;An agent that can read your systems and act on them needs guardrails, the same way a new hire with production access needs an onboarding checklist and someone reviewing their first few changes. That's what Cognous's &lt;a href="https://dev.to/cognous/cognous-keeps-your-ai-in-check-69l"&gt;Open Control Stack&lt;/a&gt; is: a &lt;a href="https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97"&gt;manifest&lt;/a&gt; declares what an agent may do before it ever runs, a &lt;a href="https://dev.to/cognous/cognous-control-plane-the-layer-that-tells-your-agent-no-3ln9"&gt;control plane&lt;/a&gt; enforces that live, and every run leaves behind a &lt;a href="https://dev.to/cognous/the-replay-bundle-that-remembers-what-happened-48l1"&gt;replay bundle&lt;/a&gt; — a signed file recording exactly what the agent proposed, what got decided, and what it relied on, so anyone can check it later.&lt;/p&gt;

&lt;p&gt;That's real technical proof, and it's the file you'd reach for in a deployment review — except the person running that review isn't going to open a JSON file and check whether a signature is valid. They're going to ask what the agent is for, what it can touch, what got blocked, and whether you can prove it, and they want that answer in a document, not a file they have to parse.&lt;/p&gt;

&lt;p&gt;That's the gap Agent Governance Evidence Pack closes. It doesn't record anything at runtime and it doesn't replace the replay bundle. It's where someone — a human, for now — turns the technical record into the document a risk committee actually reads.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Report Itself
&lt;/h2&gt;

&lt;p&gt;Cognous's tooling for this layer is &lt;code&gt;agep&lt;/code&gt; — a schema, a validator, and a renderer. Run &lt;code&gt;agep render&lt;/code&gt; on a completed pack and this is what comes out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;agep render support_agent_evidence_pack.json &lt;span class="nt"&gt;--out&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 1. Executive Summary&lt;/span&gt;

&lt;span class="gs"&gt;**SupportAgent**&lt;/span&gt; is deployed in the &lt;span class="gs"&gt;**production**&lt;/span&gt; environment. Business
purpose: Reduce first-response time on support tickets by pulling account
context and drafting a reply for a human to send.

The agent has access to &lt;span class="gs"&gt;**3**&lt;/span&gt; tool(s) and can propose &lt;span class="gs"&gt;**3**&lt;/span&gt; action(s), of
which &lt;span class="gs"&gt;**2**&lt;/span&gt; require authority or human review.

&lt;span class="gs"&gt;**1**&lt;/span&gt; replay bundle(s) are available (1 signed).
Validation summary: 1 valid bundle(s), 0 invalid, 0 error(s), 1 warning(s).

&lt;span class="gs"&gt;**1**&lt;/span&gt; open risk(s) (0 critical).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rest of the report breaks into eight areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent and Deployment Overview&lt;/strong&gt; — who owns the agent, why it exists, and where it's running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool and Action Inventory&lt;/strong&gt; — every tool the agent can reach and every action it can propose, each flagged for whether it needs authority or review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authority Model&lt;/strong&gt; — the rules of authority itself: what counts as privileged, how a grant gets requested, whether it expires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy Controls&lt;/strong&gt; — the mechanisms actually implemented to enforce those rules, each with a status and an evidence reference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blocked-Action and Reliance Summaries&lt;/strong&gt; — what got stopped and why, and what data the agent leaned on to do its job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replay Bundle Inventory and Validation Summary&lt;/strong&gt; — which replay bundles back this report up, and whether those bundles passed validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redaction and Export Summary&lt;/strong&gt; — a record that the replay bundle was redacted for an outside recipient: which fields were stripped and where the redacted file lives, not the redacted content itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk Register&lt;/strong&gt; — open risks, each tied back to real evidence rather than asserted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two sections are left off that list on purpose. Review Records is empty because this pack is still &lt;code&gt;in_review&lt;/code&gt; — a review record only exists once someone's actually signed off, and nobody has yet. Known Limitations is fixed text the renderer appends to every pack regardless of content, not something specific to this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing Builds This For You
&lt;/h2&gt;

&lt;p&gt;The evidence pack is built from a JSON file. That file is data-driven, but human-curated: it incorporates data pulled from the replay bundle — the block count, the reliance record, and so on.&lt;/p&gt;

&lt;p&gt;Pulling those raw facts out of the bundle takes a script written for this workflow. It's not part of the evidence pack format itself.&lt;/p&gt;

&lt;p&gt;As an example, &lt;code&gt;send_email&lt;/code&gt; got blocked once. The script counts that, and pulls the raw reason logged for it: &lt;code&gt;Tool 'send_email' is explicitly blocked in this frame.&lt;/code&gt; That's as far as the script gets. The schema doesn't leave room to add anything more structured than that — a blocked-action record has exactly one free-text field for explaining what happened, &lt;code&gt;reason_summary&lt;/code&gt;, and nothing else. So if you also want the record to say &lt;em&gt;why&lt;/em&gt; nothing overrode the block — that no authority was ever granted for it — the only place that fact can go is into that same sentence, written by a person.&lt;/p&gt;

&lt;p&gt;Here's what that produced, with each field marked for where it came from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"send_customer_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external_send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"send_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason_summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email send blocked: send_email is explicitly blocked in this frame and no authority grant for the email.send.customer scope exists."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run:e6d9a08e-40cf-4b4f-b4dd-82201bc15901"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;tool_name&lt;/code&gt;, &lt;code&gt;action_type&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, and &lt;code&gt;evidence_reference&lt;/code&gt; all come straight from the bundle. &lt;code&gt;action_name&lt;/code&gt; is a readable label a person chose — the bundle only knows the tool as &lt;code&gt;send_email&lt;/code&gt;. &lt;code&gt;reason_summary&lt;/code&gt; is the one sentence written from scratch.&lt;/p&gt;

&lt;p&gt;The same script could call an LLM to write &lt;code&gt;reason_summary&lt;/code&gt; instead of a person — combining a couple of facts into one sentence is exactly what an LLM is good at. Cognous has a replay-bundle import helper and "richer executive summaries — enhanced narrative generation" on the roadmap, so a script-plus-LLM setup is a reasonable stopgap until that lands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation Catches What You Forget
&lt;/h2&gt;

&lt;p&gt;Once a pack is built, &lt;code&gt;agep validate&lt;/code&gt; checks it against the schema's own rules — things like "if an action requires authority, the pack has to say what that authority actually is." The first draft of this pack didn't say that, and validation caught it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;agep validate support_agent_evidence_pack.json
Pack ID : ep-support-agent-2026-001
Valid   : False
Issues  : 4
  &lt;span class="o"&gt;[&lt;/span&gt;ERROR] E018 &lt;span class="o"&gt;[&lt;/span&gt;authority_model]: One or more actions have &lt;span class="nv"&gt;authority_required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;True but authority_model is missing.
  &lt;span class="o"&gt;[&lt;/span&gt;WARNING] W014 &lt;span class="o"&gt;[&lt;/span&gt;review_records]: review_records is empty.
  &lt;span class="o"&gt;[&lt;/span&gt;WARNING] W019 &lt;span class="o"&gt;[&lt;/span&gt;action_inventory]: Action &lt;span class="s1"&gt;'draft_ticket_reply'&lt;/span&gt; is &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'write'&lt;/span&gt; but authority_required is False.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error is straightforward: &lt;code&gt;send_customer_email&lt;/code&gt; was marked as needing authority, but nothing in the pack said what that authority was. That's not something the script would catch — it only reads replay bundles, and a replay bundle doesn't carry that information. It exists, though: the manifest declares it, back at the Declare layer, as the &lt;code&gt;email.send.customer&lt;/code&gt; scope. The script just never looks there. The fix is adding it by hand: the &lt;code&gt;email.send.customer&lt;/code&gt; scope from the manifest, with a note that no grant exists yet for this deployment. That note is accurate. It's the real reason the send got blocked instead of just flagged for review.&lt;/p&gt;

&lt;p&gt;W019 is worth a one-line note: the validator's docs undersell which action types trigger it — the code also flags plain &lt;code&gt;write&lt;/code&gt; actions like &lt;code&gt;draft_ticket_reply&lt;/code&gt;, not just external sends. The warning is left standing rather than papered over, because &lt;code&gt;draft_reply&lt;/code&gt; genuinely has no authority gate today.&lt;/p&gt;

&lt;p&gt;With the missing authority section added, the pack validates clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;agep validate support_agent_evidence_pack.json
Valid   : True
Issues  : 3
  &lt;span class="o"&gt;[&lt;/span&gt;WARNING] W014 &lt;span class="o"&gt;[&lt;/span&gt;review_records]: review_records is empty.
  &lt;span class="o"&gt;[&lt;/span&gt;WARNING] W015 &lt;span class="o"&gt;[&lt;/span&gt;review_status]: Production deployment has review_status &lt;span class="s1"&gt;'draft'&lt;/span&gt; or &lt;span class="s1"&gt;'in_review'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;WARNING] W019 &lt;span class="o"&gt;[&lt;/span&gt;action_inventory]: Action &lt;span class="s1"&gt;'draft_ticket_reply'&lt;/span&gt; is &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'write'&lt;/span&gt; but authority_required is False.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three remaining warnings are honest, not bugs to fix. There's no review record because nobody has reviewed this pilot run yet. &lt;code&gt;draft_reply&lt;/code&gt; genuinely has no authority gate. A pack with zero warnings at this stage would be hiding something, not proving something.&lt;/p&gt;

&lt;p&gt;That gap is also the pack's one open risk. The Risk Register says the same thing in plain terms: &lt;code&gt;draft_reply&lt;/code&gt; needs manual review on every single invocation, because it was never added to the control plane's allowed-tools list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirming the Evidence
&lt;/h2&gt;

&lt;p&gt;The last step is a manual approval. Recall that the &lt;code&gt;Review Records&lt;/code&gt; section sits empty right now. Once validation passes, a human — typically a CISO or Risk Manager — reviews the pack and signs off, adding a &lt;code&gt;ReviewRecord&lt;/code&gt;: their name, their role, a decision, and a timestamp. That's what moves &lt;code&gt;review_status&lt;/code&gt; from &lt;code&gt;in_review&lt;/code&gt; to &lt;code&gt;approved&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The schema enforces this, not just documents it. A pack can't be marked &lt;code&gt;approved&lt;/code&gt; without that record on file, and a critical, unresolved risk in the register blocks approval outright, no matter who signed off.&lt;/p&gt;

&lt;p&gt;With the official sign-off, the evidence pack is ready for whatever comes next — an audit, a board review, or wider deployment of the agent it covers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving The Whole Stack Held
&lt;/h2&gt;

&lt;p&gt;In this series, we've covered the four layers of Cognous's &lt;a href="https://dev.to/cognous/cognous-keeps-your-ai-in-check-69l"&gt;Open Control Stack&lt;/a&gt;: a &lt;a href="https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97"&gt;manifest&lt;/a&gt; that declares what an agent may do, a &lt;a href="https://dev.to/cognous/cognous-control-plane-the-layer-that-tells-your-agent-no-3ln9"&gt;control plane&lt;/a&gt; that enforces it live, a &lt;a href="https://dev.to/cognous/the-replay-bundle-that-remembers-what-happened-48l1"&gt;replay bundle&lt;/a&gt; that proves a specific run followed the rules, and an evidence pack that turns all of that into something a non-engineer can actually approve or reject. Each layer produces something real the next one consumes — sometimes with a clean mapping, sometimes, like this one, with a script and a person doing the work no importer does yet.&lt;/p&gt;

&lt;p&gt;Clone &lt;a href="https://github.com/cogno-us/cognous-agent-governance-evidence-pack" rel="noopener noreferrer"&gt;Agent Governance Evidence Pack&lt;/a&gt; and try &lt;code&gt;agep&lt;/code&gt; against the example packs in the repo, or build one from your own control plane and replay bundle records.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Replay Bundle That Remembers What Happened</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Tue, 25 Aug 2026 12:58:42 +0000</pubDate>
      <link>https://dev.to/cognous/the-replay-bundle-that-remembers-what-happened-48l1</link>
      <guid>https://dev.to/cognous/the-replay-bundle-that-remembers-what-happened-48l1</guid>
      <description>&lt;p&gt;It's 2am. A customer complains about something an agent did. The next morning the CEO is already asking the only question that matters: did the agent do something it shouldn't have?&lt;/p&gt;

&lt;p&gt;"I'm pretty sure our guardrails are solid" isn't an answer anyone wants to give the boss. It certainly won't fly with a customer, an auditor, or with legal. What they want is proof, and proof means being able to answer specifically: what did the agent try to do, on this exact run, and what happened when it tried?&lt;/p&gt;

&lt;p&gt;If that answer lives in the timestamps of a few hundred servers and a half-dozen tool integrations, you don't have proof. You have a scavenger hunt. "Trust us, it's fine" does not survive a scavenger hunt.&lt;/p&gt;

&lt;p&gt;Cognous' Open Control Stack exists to close that gap. It follows four steps: Declare → Control → Replay → Evidence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declare:&lt;/strong&gt; Agents need guardrails to tell them what they can and cannot do — that's a &lt;a href="https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97"&gt;manifest&lt;/a&gt;, declared up front.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control:&lt;/strong&gt; They need something enforcing those guardrails live — that's a &lt;a href="https://dev.to/cognous/cognous-control-plane-the-layer-that-tells-your-agent-no-3ln9"&gt;control plane&lt;/a&gt;, deciding allow/block/escalate on every proposal in real time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we'll cover Replay: no longer defining and patrolling the guardrails, but the receipts that the rails held. Taking what the Control Plane recorded during a run and turning it into one portable file — the thing you actually hand someone instead of a text file with pasted logs from a dozen systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What The Control Plane Hands Off
&lt;/h2&gt;

&lt;p&gt;Picture a workflow mid-run: actions getting proposed, the gate deciding each one, live.&lt;/p&gt;

&lt;p&gt;Now what? The Control Plane made the right calls in the moment — but how do you actually know that, after the fact, without having watched it happen live? That's the question Replay answers: it takes what already happened and turns it into something you can go back and inspect.&lt;/p&gt;

&lt;p&gt;The Control Plane keeps a recorder running for the life of a run — that's what's actually holding the frame, the proposals, the decisions, the traces as they happen. At the end of the run, the recorder exports everything it captured as a replay bundle: one file you can store, ship, or hand off for later analysis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_replay_bundle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_bundle.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;run_bundle.json&lt;/code&gt; is the full playback of the workflow — every decision, every trace, and the evidence behind the one blocked action, &lt;code&gt;send_email&lt;/code&gt;, all in one place. It's exactly what you'd look at to understand an error, or hand over if there's a deeper investigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Replay Tool
&lt;/h3&gt;

&lt;p&gt;Agent Replay Bundle ships its own CLI, &lt;code&gt;arb&lt;/code&gt;, for working with files like this one — validating, summarizing, redacting, signing. Start with validation, pointed at the bundle the Control Plane just exported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;arb validate run_bundle.json
Error: Replay bundle failed model validation: 1 validation error &lt;span class="k"&gt;for &lt;/span&gt;AgentReplayBundle
bundle_id
  Field required &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;missing]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fails immediately. The Control Plane's export calls the field &lt;code&gt;replay_bundle_id&lt;/code&gt;; &lt;code&gt;arb&lt;/code&gt;'s schema — the &lt;code&gt;AgentReplayBundle&lt;/code&gt; model that &lt;code&gt;validate&lt;/code&gt; checks against — calls it &lt;code&gt;bundle_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Agent Replay Bundle is a public interchange format, meant for any system, and Agent Control Plane's internal run record shapes several of the schema terms differently than the Replay Bundle. Running validation means performing a mapping step between the two first — small, mostly renames:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;mapped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bundle_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replay_bundle_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generated_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generated_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action_proposals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;actions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;policy_decisions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decisions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;policy_traces&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;policy_traces&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authority_records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authority_records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reliance_records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reliance_records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blocked_actions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blocked_actions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;final_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;final_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run that, and the same run record now validates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;arb validate mapped_replay_bundle.json
VALID  &lt;span class="nv"&gt;bundle_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ae564206-8daf-4b19-9d60-db54f56387a5  &lt;span class="nv"&gt;issues&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
  WARNING W005: Bundle has no authority records.
  WARNING W012: signature_metadata is missing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid — with two warnings, not zero. Neither one blocks validity, and both are honest about the bundle's actual state: no authority was granted for this run (which is &lt;em&gt;why&lt;/em&gt; two of the three actions didn't just allow), and nothing's been signed yet.&lt;/p&gt;

&lt;p&gt;Worth building into your own pipeline: none of this runs automatically. &lt;code&gt;generate_replay_bundle()&lt;/code&gt; builds the object; &lt;code&gt;arb validate&lt;/code&gt; is a separate command a human has to think to run. Nothing stops a bundle from sitting unvalidated for months until the day someone actually needs it — which is the worst possible time to find out it doesn't pass. Wire the mapping and the validation into the end of every run, not into the moment an auditor asks for one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What A Reviewer Sees First
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;arb summarize&lt;/code&gt; is the one-screen version — what you'd look at before deciding whether to open the full bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;arb summarize mapped_replay_bundle.json
bundle_id:          ae564206-8daf-4b19-9d60-db54f56387a5
run_id:             c3f474dd-952b-4f27-ab5c-98de868b3c67
status:             &lt;span class="nb"&gt;complete
&lt;/span&gt;actor:              support-agent-v1
environment:        production
policy_version:     v1.0
action_proposals:   3
policy_decisions:   3
policy_traces:      3
blocked_actions:    1
authority_records:  0
reliance_records:   1
final_output:       present
redacted:           no
signed:             no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three proposals, three decisions, one block, zero authority records. That last number is the tell — it's the same fact the warning surfaced, now visible at a glance instead of buried in a rule name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redacting For Export
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mapped_replay_bundle.json&lt;/code&gt; — the bundle we just validated — still has raw targets and payloads in it: customer IDs, draft email bodies, potentially PII. Fine for an internal review, not fine to hand to outside counsel.&lt;/p&gt;

&lt;p&gt;Redaction isn't content-aware — it doesn't scan text and detect what looks sensitive. It replaces a fixed set of fields wholesale: &lt;code&gt;payload&lt;/code&gt; (redacted by default), plus &lt;code&gt;target&lt;/code&gt; and &lt;code&gt;final_output&lt;/code&gt; if you opt in with flags. Everything structural — IDs, timestamps, decision results, policy names — is preserved no matter what:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;arb redact mapped_replay_bundle.json &lt;span class="nt"&gt;--out&lt;/span&gt; redacted_bundle.json &lt;span class="nt"&gt;--targets&lt;/span&gt; &lt;span class="nt"&gt;--final-output&lt;/span&gt;
Redacted bundle written to redacted_bundle.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"redaction_metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redacted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redacted_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-18T19:47:34Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redacted_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"action_proposals[0].payload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"action_proposals[0].target"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"action_proposals[1].payload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"action_proposals[1].target"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"action_proposals[2].payload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"action_proposals[2].target"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"final_output"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"replacement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[REDACTED]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A redacted bundle still validates clean — the whole point is that a reviewer can see &lt;em&gt;what&lt;/em&gt; was blocked and &lt;em&gt;why&lt;/em&gt;, without seeing the customer's actual email address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signing For Export Integrity
&lt;/h2&gt;

&lt;p&gt;Once a bundle is ready to leave the building, sign it so anyone downstream can tell if it was altered in transit — the redacted version, since that's the one actually going out the door:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;arb sign redacted_bundle.json &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="s2"&gt;"demo-secret"&lt;/span&gt; &lt;span class="nt"&gt;--key-id&lt;/span&gt; &lt;span class="s2"&gt;"cognous-demo-key-01"&lt;/span&gt; &lt;span class="nt"&gt;--out&lt;/span&gt; signed_bundle.json
Signed bundle written to signed_bundle.json

&lt;span class="nv"&gt;$ &lt;/span&gt;arb verify signed_bundle.json &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="s2"&gt;"demo-secret"&lt;/span&gt;
Signature VALID

&lt;span class="nv"&gt;$ &lt;/span&gt;arb verify signed_bundle.json &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="s2"&gt;"wrong-secret"&lt;/span&gt;
Signature INVALID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's HMAC-SHA256 over a canonical serialization of the bundle. Worth being precise about what it proves: it tells you the bundle hasn't changed since it was signed, given the secret. It does not prove &lt;em&gt;who&lt;/em&gt; signed it — there's no identity binding, no PKI, no protection if the secret itself leaks. For production use, that's a job for a real key management system. For a shared-secret export check between two parties who already trust each other, HMAC does what it says.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading The Verified File
&lt;/h2&gt;

&lt;p&gt;The auditor gets &lt;code&gt;signed_bundle.json&lt;/code&gt;, runs &lt;code&gt;verify&lt;/code&gt;, and knows immediately whether it matches what left your hands. Signing isn't encryption, though — &lt;code&gt;verify&lt;/code&gt; is the only command that speaks the signed wrapper (&lt;code&gt;signed_bundle_id&lt;/code&gt;, &lt;code&gt;replay_bundle&lt;/code&gt;, &lt;code&gt;signature_metadata&lt;/code&gt;). To actually read the run, pull the inner bundle back out first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signed_bundle.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;signed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extracted.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replay_bundle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;extracted.json&lt;/code&gt; is a plain &lt;code&gt;AgentReplayBundle&lt;/code&gt; again — &lt;code&gt;summarize&lt;/code&gt;, &lt;code&gt;validate&lt;/code&gt;, everything else works on it. Verify first, extract second: confirm nothing's been tampered with before you trust a single field inside it.&lt;/p&gt;

&lt;p&gt;The signature also doesn't prove anything about the gap between when the run happened and when you got around to signing it. &lt;code&gt;signed_at&lt;/code&gt; is just whatever time you ran the &lt;code&gt;sign&lt;/code&gt; command — there's no check against &lt;code&gt;generated_at&lt;/code&gt;. A bundle that sits unsigned for three days is three days an editor could have touched the file with no trace of it; sign it on day three, and &lt;code&gt;verify&lt;/code&gt; comes back clean regardless. The signature only covers the window it's actually been alive for. So the real answer isn't "sign it before you send it" — it's validate, redact, and sign as part of the run itself, the moment the bundle is generated, so there's no gap for anything to slip into.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving The Guardrails Held
&lt;/h2&gt;

&lt;p&gt;Back to that morning-after meeting: the complaint, and the question from the boss. What was once a stressful scavenger hunt across servers matching timestamps and logfiles is now just opening one file.&lt;/p&gt;

&lt;p&gt;Agentic runs get their guardrails from two layers: a manifest declaring what's allowed, and a control plane enforcing that live. Replay is the layer that lets your team prove those guardrails actually held — turning what the control plane recorded into one validated, signed file anyone can check independently, long after the run itself is over.&lt;/p&gt;

&lt;p&gt;Clone &lt;a href="https://github.com/cogno-us/cognous-agent-replay-bundle" rel="noopener noreferrer"&gt;Agent Replay Bundle&lt;/a&gt; and try &lt;code&gt;arb&lt;/code&gt; against the example bundles in the repo, or your own control plane exports.&lt;/p&gt;

&lt;p&gt;Learn more about Agent Replay Bundle at &lt;a href="https://cogno.us/agent_replay_bundle.html" rel="noopener noreferrer"&gt;cogno.us&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>security</category>
    </item>
    <item>
      <title>Cognous Control Plane: The Layer That Tells Your Agent No</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Tue, 18 Aug 2026 19:39:36 +0000</pubDate>
      <link>https://dev.to/cognous/cognous-control-plane-the-layer-that-tells-your-agent-no-3ln9</link>
      <guid>https://dev.to/cognous/cognous-control-plane-the-layer-that-tells-your-agent-no-3ln9</guid>
      <description>&lt;p&gt;It's all too common — a viral tweet describing how AI agents with poorly defined guardrails deleted production, wiped a critical database, or caused some other disastrous occurrence. Agents need guardrails: this includes permissions to do the job, but also blockers that limit the reach of the agent, thereby preventing accidental disasters that fuel viral tweets.&lt;/p&gt;

&lt;p&gt;Cognous' Open Control Stack is a framework for wrapping AI agents in guardrails, providing access where needed, but blocking the boundaries that cannot be crossed.&lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://dev.to/cognous/cognous-keeps-your-ai-in-check-69l"&gt;introductory blog post&lt;/a&gt;, our agent drafted a cringey follow-up email and "helpfully" sent it to a company's entire list of top customers. Nobody approved that send, and the human marketing team had to drop everything and work to mitigate the damage.&lt;/p&gt;

&lt;p&gt;Open Control Stack is structured as four layers: &lt;strong&gt;Declare → Control → Replay → Evidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We built the first layer in &lt;a href="https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97"&gt;The Manifest That Keeps Your AI Agent Honest&lt;/a&gt;, the &lt;strong&gt;Agent Action Manifest&lt;/strong&gt;. That's the Declare layer — a JSON file that works as the allow/block list for our agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pull the top customer:&lt;/strong&gt; allowed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draft a reply:&lt;/strong&gt; draft-first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull contract details:&lt;/strong&gt; blocked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send the email:&lt;/strong&gt; needs approval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that's all a manifest is — a list. Nothing reads it and nothing enforces it. We need something that takes that list and actually applies it to the agent while it's running.&lt;/p&gt;

&lt;p&gt;That's the &lt;strong&gt;Agent Control Plane&lt;/strong&gt; — the Control layer. This post is about what it actually does when the agent tries something.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sits Beside The Agent
&lt;/h2&gt;

&lt;p&gt;The Control Plane doesn't run the agent, and it isn't a framework. It sits next to whatever is already generating the agent's behavior — OpenClaw, Hermes, a custom loop, whatever — and turns every proposed action into a recorded decision before that action is treated as real.&lt;/p&gt;

&lt;p&gt;The agent's job is small in this picture — it proposes. The Control Plane does the rest: it decides, and it documents. Propose is the only row the agent touches. Decide is the gate ruling allow, block, or escalate. Document is what gets written down once the decision's made. The agent doesn't get a say in the outcome — it just gets to ask.&lt;/p&gt;

&lt;p&gt;The pattern, end to end:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Propose
&lt;/h3&gt;

&lt;p&gt;The agent wants to do something. The Control Plane frames it, and a proposal gets built.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent Task:&lt;/strong&gt; The agent kicks off the run. In our use case: "review a customer account and follow up."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frame:&lt;/strong&gt; The Control Plane frames the execution context for the run — the task, actor, environment, allowed tools, blocked tools, policy version. This gets locked in at the start and becomes immutable for the life of the run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Proposal:&lt;/strong&gt; The agent's ask — this tool, this action type, this target, this payload, this reason. Recorded before anything executes — that's the point of the proposal steps. If the decision gate blocks it next, nothing downstream ever sees a real tool call, but the attempt is still on the record.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Decide
&lt;/h3&gt;

&lt;p&gt;The Control Plane decides whether that proposal is actually okay. This is where the manifest's declarations actually take effect: what it declared is what ends up in the frame's allowed and blocked lists, and the frame is what the gate checks against. Two of the six rules below check something separate from the frame: authority — whether this actor currently has permission to write, or to send things externally. Authority is granted per run, on top of the frame, and it's what rules 3 and 5 are checking for.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Policy Gate:&lt;/strong&gt; Decides what happens to the proposal. Six rules, checked top to bottom, first match wins:

&lt;ol&gt;
&lt;li&gt;tool is explicitly blocked → &lt;code&gt;block&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;tool is not in the allowed list → &lt;code&gt;escalate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;action type is &lt;code&gt;external_send&lt;/code&gt; → &lt;code&gt;allow&lt;/code&gt; if authority for it exists, otherwise &lt;code&gt;block&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;action type is &lt;code&gt;read&lt;/code&gt; (and passed rules 1–2) → &lt;code&gt;allow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;action type is &lt;code&gt;write&lt;/code&gt; → &lt;code&gt;allow&lt;/code&gt; if authority for it exists, otherwise &lt;code&gt;escalate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;anything else → &lt;code&gt;escalate&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy Decision + Evaluation Trace:&lt;/strong&gt; The gate's verdict, plus the rule-by-rule path it took to reach it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allow / Block / Escalate:&lt;/strong&gt; The three possible verdicts. Only &lt;code&gt;allow&lt;/code&gt; lets the proposal go on to actually touch a tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Document
&lt;/h3&gt;

&lt;p&gt;Once a decision is made, the Control Plane writes down what happened, what the decision was, and how it was made.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reliance Record:&lt;/strong&gt; Once an action is allowed and actually executed, a record of what the agent depended on to produce its output — a tool, a database, a file, an API, user input. Blocked and escalated actions never execute, so there's nothing to record reliance on — dependence doesn't exist until an action actually runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run Record:&lt;/strong&gt; Every proposal, decision, trace, block, and reliance from this run, assembled into one object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replay Bundle:&lt;/strong&gt; The run record packaged up for later, portable inspection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running The Scenario For Real
&lt;/h2&gt;

&lt;p&gt;Let's take three of those actions from the manifest and actually run them through the gate: &lt;code&gt;pull_top_customers&lt;/code&gt;, &lt;code&gt;draft_reply&lt;/code&gt;, and &lt;code&gt;send_email&lt;/code&gt;. All three are on this run's allowed-tools list. &lt;code&gt;pull_contract_details&lt;/code&gt; is also declared in the frame's blocked-tools list, matching the manifest — but the task is "review a customer account and follow up," which never calls for contract details, so the agent never proposes it. It's declared, not exercised: nothing for the gate to evaluate here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Starting the run:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the Control Plane spinning up: &lt;code&gt;RunRecorder()&lt;/code&gt; creates the recorder instance, and &lt;code&gt;start_run()&lt;/code&gt; builds the frame — the same frame from the Propose section above, locking in the task, actor, environment, allowed tools, blocked tools, and policy version for everything that follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_control_plane&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RunRecorder&lt;/span&gt;

&lt;span class="n"&gt;recorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RunRecorder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;run_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review a customer account and follow up.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;support-agent-v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allowed_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pull_top_customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;draft_reply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send_email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;blocked_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pull_contract_details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;policy_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v1.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Proposing and Deciding One Action:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the pattern for a single action, &lt;code&gt;pull_top_customers&lt;/code&gt;. The same process repeats for the other two actions — omitted here for brevity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Propose the action with the &lt;code&gt;propose_action&lt;/code&gt; method. This is stored in the variable &lt;code&gt;a1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Evaluate the proposal with &lt;code&gt;evaluate_action&lt;/code&gt;. This returns two values: the decision, and, if the action is blocked, a blocked-action record.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;a1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;propose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pull_top_customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;crm:top_customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Identify the customer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s account tier before drafting a reply.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pull_top_customers&lt;/code&gt; is not blocked, so we receive only the decision, and the unused second value gets thrown away with &lt;code&gt;_&lt;/code&gt;. &lt;code&gt;d1&lt;/code&gt; holds the decision: the verdict (&lt;code&gt;allow&lt;/code&gt;, &lt;code&gt;block&lt;/code&gt;, or &lt;code&gt;escalate&lt;/code&gt;), which rule produced it, and why. Here's what the gate decides across all three actions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pull_top_customers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;read&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draft_reply&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;write&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;escalate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;send_email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;external_send&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;block&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Missing authority is what stops two of the three from just allowing. &lt;code&gt;draft_reply&lt;/code&gt; escalates and &lt;code&gt;send_email&lt;/code&gt; blocks — same problem, nobody granted permission for this run — but different severity, because the two actions aren't equally reversible. A blocked send is stopped outright: once an email's out, it's out. An escalated write just doesn't execute yet. Nothing about the original decision ever changes — but if someone grants write authority before this run ends, a fresh proposal for the same action would clear rule 5 as &lt;code&gt;allow&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The evaluation trace is where the reasoning survives, not just the verdict. Here's the trace for the &lt;code&gt;send_email&lt;/code&gt; decision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules_evaluated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"rule_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked_tool_policy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"matched"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"rule_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unknown_tool_policy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"matched"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rule_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external_send_authority_policy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Action type 'external_send' requires an authority record with scope 'external_send', which was not found."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"final_result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reviewer looking at this trace can tell exactly which check failed and why — not just that the action was blocked. That matters because &lt;code&gt;send_email&lt;/code&gt; is on the &lt;em&gt;allowed&lt;/em&gt;-tools list; nothing about the tool itself was forbidden. The trace is what proves the block was about missing authority, not a banned tool.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Worth noting:&lt;/strong&gt; &lt;code&gt;draft_reply&lt;/code&gt; escalating isn't a lesser outcome than blocking. It's easy to read "escalate" as a downgrade of "block," but it's really the gate saying it doesn't have enough information to decide safely, so a human should. Grant &lt;code&gt;write&lt;/code&gt; authority to the run, and the same action clears rule 5 as &lt;code&gt;allow&lt;/code&gt; instead — the rule didn't change; what the actor is authorized to do did.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Documenting The Proposal And Decision
&lt;/h2&gt;

&lt;p&gt;After a decision has been made, the Cognous Control Plane documents what occurred during the process. All of the information is collated and stored in one place — not grabbed from logs scattered across different tools and pieced together by timestamp.&lt;/p&gt;

&lt;p&gt;The first step is the Reliance Record. Recall that this only documents actions that actually ran — in this case, &lt;code&gt;pull_top_customers&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_reliance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;source_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pull_top_customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer tier and account fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;referenced_action_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't get written to a file by itself — it's held in memory as part of the run, alongside every proposal, decision, and trace, until the whole thing gets closed out and exported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Draft prepared and escalated for review. Email send was blocked, not attempted.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_record.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_replay_bundle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;export_json()&lt;/code&gt; writes everything — proposals, decisions, traces, the one blocked-action record, that one reliance record — to a &lt;code&gt;RunRecord&lt;/code&gt; JSON file. Retrieving the reliance record later is just reading that file and looking at its &lt;code&gt;reliance_records&lt;/code&gt; array.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading The Results
&lt;/h2&gt;

&lt;p&gt;Writing the record is only half the story — you also need a way to check it. That's &lt;code&gt;acp&lt;/code&gt;, the Control Plane's own command-line tool, shipped in the same repo: &lt;a href="https://github.com/cogno-us/cognous-agent-control-plane" rel="noopener noreferrer"&gt;github.com/cogno-us/cognous-agent-control-plane&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running it against the run record we just exported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;acp validate-run run_record.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RunRecord 79ad6860-03de-4136-9828-d4e40fbd05f4: valid (0 errors, 0 warnings)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validation here isn't checking whether the run succeeded — it's checking that the record is internally consistent: every decision references an action that actually exists, every blocked action corresponds to a decision that actually says &lt;code&gt;block&lt;/code&gt;. It's a check on the evidence, not on the outcome.&lt;/p&gt;

&lt;p&gt;The replay bundle gets the same treatment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;acp validate-replay replay_bundle.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ReplayBundle 535c72f8-efee-4144-9b78-7f7fff73133d: valid (0 errors, 0 warnings)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What This Deliberately Doesn't Do
&lt;/h2&gt;

&lt;p&gt;The Control Plane only enforces the rules and authority it's actually been given — it doesn't infer intent, and it doesn't fill gaps with a best guess. A permission that was never granted shows up as an escalation or a block, not a pass. It's not a compliance system, and it's not a guarantee that the agent's output was correct. It's the layer that turns "the agent tried to do X" into a recorded, deterministic, inspectable decision, every time.&lt;/p&gt;

&lt;p&gt;It also doesn't decide what happens after a decision is made. The gate doesn't open a ticket, notify anyone, or track whether an escalation ever gets reviewed. Whether a human looks at the draft, whether the agent retries, whether authority eventually gets granted — that's handled by whatever's orchestrating the agent, not by the Control Plane.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Up
&lt;/h2&gt;

&lt;p&gt;We now have a run: three proposed actions, one blocked, one escalated, one allowed, each with a trace and a fingerprint, packaged into a replay bundle that validates clean. That bundle is a record of one run.&lt;/p&gt;

&lt;p&gt;The Cognous Open Control Stack has four steps: Declare → Control → Replay → Evidence.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97"&gt;The Manifest That Keeps Your AI Agent Honest&lt;/a&gt;, we declared in the manifest what was allowed and what was not. In this post, we walked through the Control Plane — it runs next to your agentic framework to allow, escalate, or block actions. The Control Plane records the results from every action taken.&lt;/p&gt;

&lt;p&gt;Next, we look at the last two steps: Replay and Evidence. These are the tools that will save you when auditors are knocking at the door, or you need to prove you've redacted customer data, or just prove it hasn't been tampered with since it was generated. That's the &lt;strong&gt;Agent Replay Bundle&lt;/strong&gt;, and we'll cover it in our next post.&lt;/p&gt;

&lt;p&gt;In the meantime, the Control Plane repo, examples, schemas, and test suite are live now: &lt;a href="https://github.com/cogno-us/cognous-agent-control-plane" rel="noopener noreferrer"&gt;github.com/cogno-us/cognous-agent-control-plane&lt;/a&gt;. Clone it, run &lt;code&gt;examples/simple_agent_run.py&lt;/code&gt;, and watch the gate make the call.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>security</category>
    </item>
    <item>
      <title>The Manifest That Keeps Your AI Agent Honest</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Wed, 12 Aug 2026 14:50:04 +0000</pubDate>
      <link>https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97</link>
      <guid>https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97</guid>
      <description>&lt;p&gt;In our first post, &lt;a href="https://dev.to/cognous/cognous-keeps-your-ai-in-check-69l"&gt;Cognous Keeps Your AI in Check&lt;/a&gt;, we introduced the scenario: a customer-service agent that pulls CRM records, drafts replies, and — if nobody's watching — occasionally sends 1,000 tone-deaf emails to your best clients. We said the fix isn't reading what the agent wrote after the fact. It's deciding, in advance, what the agent is even allowed to attempt.&lt;/p&gt;

&lt;p&gt;That decision has to live somewhere. It can't live in a Slack thread, a comment in the agent's system prompt, or "Dave from platform remembers we blocked that." It needs to be a file — versioned, reviewable, and boring enough that a security team can actually sign off on it.&lt;/p&gt;

&lt;p&gt;That's what Cognous's Open Control Stack is for. It's four small, open-source projects — Declare, Control, Replay, Evidence — that sit beside your agents and cover authorize, enforce, and prove. Declare comes first, and its artifact is the &lt;strong&gt;Agent Action Manifest&lt;/strong&gt;. This post describes the Action Manifest tooling in the Open Control Stack, available on &lt;a href="https://github.com/cogno-us/cognous-open-control-stack" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Actions, Written Down
&lt;/h2&gt;

&lt;p&gt;If you remember in post one, our AI agent was pulling customer data, drafted a cringey email, and sent it to our top 1,000 customers. The Action Manifest is used to outline the agent's permissions as declarations, to prevent this from ever happening again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer-service-agent-manifest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer-service-agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"support-platform-team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer relationship management system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"external_system"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crm.internal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"data_classification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_pii"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Outbound customer email system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"external_system"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"smtp.internal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"data_classification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_pii"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pull_top_customers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Retrieve top customer records from the CRM."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reliance_requirement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"allowed_source_types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"draft_reply"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Draft a reply email for a customer inquiry."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"authority_required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email.draft.customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Permission to draft outbound customer emails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"support-platform-team"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"review_requirement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"draft_first"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pull_contract_details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Retrieve contract terms from the CRM."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"contract"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"restricted"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"send_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external_send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Send an approved email to the customer."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"escalate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"authority_required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email.send.customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Outbound customer send scope"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email.send.customer"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"review_requirement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approval_required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"reviewer_role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"support-lead"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reliance_requirement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"allowed_source_types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user_input"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"payload_policy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sensitive_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"customer_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"forbidden_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"contract_terms"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"redaction_hints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"field_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PII in exported records"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Field names here follow the public schema — check the &lt;a href="https://github.com/cogno-us/cognous-open-control-stack" rel="noopener noreferrer"&gt;repo&lt;/a&gt; for the exact current shape if you're implementing against it.)&lt;/p&gt;

&lt;p&gt;Rules are only as good as what is defined. At the very top of the manifest, we explicitly cover anything not mentioned: &lt;code&gt;"default_action": "block"&lt;/code&gt;. So when a new tool is onboarded (for example &lt;code&gt;send_tweet&lt;/code&gt;), you don't need to go back and update every manifest — it's blocked until you expressly give the agent permission to use it.&lt;/p&gt;

&lt;p&gt;For those actions that are defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pull_top_customers&lt;/code&gt; and &lt;code&gt;draft_reply&lt;/code&gt; are both &lt;code&gt;allow&lt;/code&gt;, but not the same kind of allow: pulling the top customers is allowed, full stop, but the draft reply has &lt;code&gt;review_requirement.mode: draft_first&lt;/code&gt; — the agent can write it, but it stops there.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;send_email&lt;/code&gt; isn't just "escalate" as a vague gesture — it names the exact authority scope required (&lt;code&gt;email.send.customer&lt;/code&gt;), the exact reviewer role (&lt;code&gt;support-lead&lt;/code&gt;), and which payload fields have to be redacted before this record goes anywhere near an audit export.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pull_contract_details&lt;/code&gt; is the odd one out — it doesn't strictly need to be there. &lt;code&gt;default_action: block&lt;/code&gt; already covers anything undeclared, so an unlisted "read contract details" action would be blocked anyway. It's included for visibility: a blocked action that shows up in the manifest, tagged &lt;code&gt;restricted&lt;/code&gt;, is a documented decision someone can point to. A blocked action that's simply never mentioned looks identical to an oversight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's important to note that the manifest is not enforcement. It doesn't stop the agent from doing anything — a runtime layer has to actually read it and act on it (that'll be described in the next post). What the manifest gives you is a single artifact that says, in one place, what &lt;em&gt;should&lt;/em&gt; happen — before the agent has run even once.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Nine Action Types
&lt;/h2&gt;

&lt;p&gt;In our simple manifest above, we show three possible action types. The manifest supports nine. These classifications are what give a governance review the ability to distinguish a harmless read from something that should never happen without a human:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;retrieves or inspects information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;creates or modifies information, not necessarily sent externally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;external_send&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;sends information outside the system boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;removes information or records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;packages or transfers information downstream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;purchase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;initiates or prepares a purchase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;approve&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;approves or authorizes a workflow step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escalate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;routes to a human or supervisory process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;other&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;anything not otherwise classified&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A tool list alone can't tell you this. Knowing the agent "has access to email" tells you nothing about whether it can draft, send, or both, and what's supposed to happen before each.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Review Modes
&lt;/h2&gt;

&lt;p&gt;Action type says &lt;em&gt;what kind&lt;/em&gt; of thing an action is. Review mode says &lt;em&gt;who has to look at it, and when&lt;/em&gt; — and it's doing just as much governance work in our example as action type is. There are four:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;no specific review posture declared&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;human_review&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a human is expected to look at it before execution or completion — no specific person named&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;approval_required&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;approval is required from a &lt;strong&gt;named reviewer role&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draft_first&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the agent produces a draft; it does not finish the action itself&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The difference between &lt;code&gt;human_review&lt;/code&gt; and &lt;code&gt;approval_required&lt;/code&gt; is easy to miss but it matters: &lt;code&gt;human_review&lt;/code&gt; says "someone should look at this" without saying who. &lt;code&gt;approval_required&lt;/code&gt; is stricter — it names a reviewer role, and the validator enforces that: an &lt;code&gt;approval_required&lt;/code&gt; action with no &lt;code&gt;reviewer_role&lt;/code&gt; fails validation outright.&lt;/p&gt;

&lt;p&gt;That's why &lt;code&gt;send_email&lt;/code&gt; in our manifest uses &lt;code&gt;approval_required&lt;/code&gt; with &lt;code&gt;reviewer_role: "support-lead"&lt;/code&gt;, not &lt;code&gt;human_review&lt;/code&gt;. Sending customer email isn't "someone should probably glance at this" — it's "this specific role signs off, or it doesn't go out."&lt;/p&gt;

&lt;p&gt;Worth being precise about what &lt;code&gt;"support-lead"&lt;/code&gt; actually is here: a role string, nothing more. The manifest doesn't know who holds that role, doesn't know they're on Slack, and doesn't page anyone. Your enterprise-side plumbing converts "support lead" to Janice, and builds an alerting system in Slack to let her know that her approval is required. The public stack's job ends at "this role must approve, and there's a record of whether it did."&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliance: A Third Thing Being Tracked
&lt;/h2&gt;

&lt;p&gt;Authority asks whether an action was &lt;em&gt;allowed&lt;/em&gt;. Review asks whether a &lt;em&gt;person&lt;/em&gt; signed off. Reliance asks something different: what did this action actually &lt;em&gt;depend on&lt;/em&gt; to produce its result?&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;reliance_requirement&lt;/code&gt; declares that an action should leave a record of its source — a tool, a database, a file, an API, or user input. &lt;code&gt;pull_top_customers&lt;/code&gt; declares reliance on &lt;code&gt;tool&lt;/code&gt; and &lt;code&gt;database&lt;/code&gt;, because a governance review should be able to see it actually came from the CRM and not somewhere the agent invented. &lt;code&gt;send_email&lt;/code&gt; declares reliance on &lt;code&gt;tool&lt;/code&gt; and &lt;code&gt;user_input&lt;/code&gt;, tying the send back to the approved draft it was built from.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;draft_reply&lt;/code&gt; and &lt;code&gt;pull_contract_details&lt;/code&gt; don't declare a reliance requirement — which is why the summarize output above shows &lt;code&gt;Actions requiring reliance: 2&lt;/code&gt;, not 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation Isn't Optional, and It Isn't Decoration
&lt;/h2&gt;

&lt;p&gt;A manifest can be valid JSON, but be semantic nonsense. A &lt;code&gt;delete&lt;/code&gt; action on the production database marked &lt;code&gt;allow&lt;/code&gt; isn't caught by JSON Schema. It's caught by the validator, which checks things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;does every action reference a tool that's actually declared and allowed?&lt;/li&gt;
&lt;li&gt;is a privileged action type (&lt;code&gt;external_send&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;purchase&lt;/code&gt;, &lt;code&gt;approve&lt;/code&gt;) missing an authority requirement it should have?&lt;/li&gt;
&lt;li&gt;is an action marked &lt;code&gt;approval_required&lt;/code&gt; but missing a reviewer role?&lt;/li&gt;
&lt;li&gt;are there duplicate action or tool names?&lt;/li&gt;
&lt;li&gt;are sensitive fields declared with no redaction hints to match?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run it from the CLI — &lt;code&gt;aam&lt;/code&gt;, short for Agent Action Manifest, is the command-line tool that ships with the manifest repo. First we run the validate command to ensure that the JSON is valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aam validate customer-service-agent-manifest.json
Validation result: VALID
Manifest ID:       customer-service-agent-manifest

Warnings &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;:
  &lt;span class="o"&gt;[&lt;/span&gt;W004] &lt;span class="o"&gt;[&lt;/span&gt;actions[1]&lt;span class="o"&gt;(&lt;/span&gt;draft_reply&lt;span class="o"&gt;)]&lt;/span&gt; Action &lt;span class="s1"&gt;'draft_reply'&lt;/span&gt; has effective default_action &lt;span class="s1"&gt;'allow'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;action_type &lt;span class="s1"&gt;'write'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;W006] &lt;span class="o"&gt;[&lt;/span&gt;actions[1]&lt;span class="o"&gt;(&lt;/span&gt;draft_reply&lt;span class="o"&gt;)&lt;/span&gt;.payload_policy] Action &lt;span class="s1"&gt;'draft_reply'&lt;/span&gt; has action_type &lt;span class="s1"&gt;'write'&lt;/span&gt; but no payload_policy is declared.
  &lt;span class="o"&gt;[&lt;/span&gt;W005] &lt;span class="o"&gt;[&lt;/span&gt;actions[2]&lt;span class="o"&gt;(&lt;/span&gt;pull_contract_details&lt;span class="o"&gt;)&lt;/span&gt;.reliance_requirement] Action &lt;span class="s1"&gt;'pull_contract_details'&lt;/span&gt; has action_type &lt;span class="s1"&gt;'read'&lt;/span&gt; but no reliance_requirement is declared.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VALID, but not silent — the validator still flags things worth a second look, even in a manifest that passes. Fair warnings, too: we didn't bother declaring a payload policy for a draft-only action, and a blocked action doesn't need reliance evidence it'll never produce.&lt;/p&gt;

&lt;p&gt;Next we can summarize the manifest with the summarize command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aam summarize customer-service-agent-manifest.json
Manifest ID:          customer-service-agent-manifest
Agent name:           customer-service-agent
Environment:          production
Tools:                2
Actions:              4

Actions by &lt;span class="nb"&gt;type&lt;/span&gt;:
  external_send: 1
  &lt;span class="nb"&gt;read&lt;/span&gt;: 2
  write: 1

Actions by default posture:
  allow: 2
  block: 1
  escalate: 1

Actions requiring authority: 2
Actions requiring review:    2
Actions requiring reliance:  2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That "2" next to authority is easy to misread as just the send — it's actually &lt;code&gt;draft_reply&lt;/code&gt; and &lt;code&gt;send_email&lt;/code&gt; both. Drafting on a customer's behalf declares its own authority requirement (&lt;code&gt;email.draft.customer&lt;/code&gt;), separate from the one on the final send (&lt;code&gt;email.send.customer&lt;/code&gt;). Declaring it isn't the same as enforcing it — nothing here checks whether that authority actually exists. That check is the Control Plane's job, next post.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aam&lt;/code&gt; verifies that your manifest is ready, and gives the team a concise summary of what's allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Deliberately Doesn't Do
&lt;/h2&gt;

&lt;p&gt;The manifest is the &lt;strong&gt;Declare&lt;/strong&gt; layer, and it stays in its lane:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it doesn't execute the agent&lt;/li&gt;
&lt;li&gt;it doesn't enforce anything at runtime&lt;/li&gt;
&lt;li&gt;it doesn't grant authority — &lt;code&gt;email.send.customer&lt;/code&gt; being &lt;em&gt;declared&lt;/em&gt; as required doesn't mean it &lt;em&gt;exists&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;it doesn't replace your application's own authorization system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Declaring that sending email should require approval is not the same thing as stopping an unapproved send. That gap — between what's declared and what actually happens when the agent tries to act — is exactly what the next layer closes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next up
&lt;/h2&gt;

&lt;p&gt;The manifest says what the agent &lt;em&gt;may&lt;/em&gt; propose. It says nothing about what the agent actually &lt;em&gt;does&lt;/em&gt; on a Tuesday afternoon when it's mid-run and reaching for the send button. That's the &lt;strong&gt;Agent Control Plane&lt;/strong&gt; — it reads this exact manifest, sits beside the agent at runtime, and turns every proposed action into a recorded decision: allow, block, or escalate, deterministically, every time. That's next.&lt;/p&gt;

&lt;p&gt;The manifest repo, examples, schemas, and test suite are live now: &lt;a href="https://github.com/cogno-us/cognous-open-control-stack" rel="noopener noreferrer"&gt;github.com/cogno-us/cognous-open-control-stack&lt;/a&gt;. Clone it, validate the customer-service example, break it on purpose and watch the validator catch you.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>security</category>
    </item>
    <item>
      <title>Cognous Keeps Your AI in Check</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Thu, 06 Aug 2026 22:49:57 +0000</pubDate>
      <link>https://dev.to/cognous/cognous-keeps-your-ai-in-check-69l</link>
      <guid>https://dev.to/cognous/cognous-keeps-your-ai-in-check-69l</guid>
      <description>&lt;p&gt;You've probably gotten an email that was supposed to feel personal, but obviously wasn't. Something about it just felt off. Automated. And somewhere in the back of your mind, you wondered: who approved this?&lt;/p&gt;

&lt;p&gt;Honestly? The answer is probably no one.&lt;/p&gt;

&lt;p&gt;Organizations are leaning harder on AI agents to actually do work. Agents are making database queries, writing letters, accessing email lists, but where does the access end? Without solid guardrails, tone-deaf emails are no longer an embarrassing edge case, they're a real risk. Nobody wants to be the team that let an agent send 1,000 tone-deaf emails to its best clients before anyone noticed.&lt;/p&gt;

&lt;p&gt;This series is about the tooling that keeps that from happening, and about actually building it, not just reading about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually needs to happen
&lt;/h2&gt;

&lt;p&gt;The email itself might be perfectly fine. Correct grammar, on-brand, even genuinely well-written. But the problem is that the email didn't need to be sent. The agent just did it anyway, without any guidance from the team.&lt;/p&gt;

&lt;p&gt;So the fix isn't a better read of what the agent said. It's governance around what the agent is allowed to do, three capabilities, specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authorize&lt;/strong&gt;: Decide in advance what an agent is allowed to do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce&lt;/strong&gt;: Apply that decision the moment the agent tries to act, before the action executes, not after.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prove&lt;/strong&gt;: Turn every run into a record you can produce on demand, not one you have to reconstruct from memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Application logs are a day late and a dollar short: the emails are already sent. Looking at logs is reactive. You need a stack that sits in front of your agents and proactively approves or denies actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Cognous
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cogno.us/" rel="noopener noreferrer"&gt;Cognous&lt;/a&gt; builds infrastructure for governing AI agents. Its first release, the Open Control Stack, is fully open source: four small projects that sit beside your AI agents to keep them in check, together covering Authorize, Enforce, and Prove. The four projects are &lt;strong&gt;Declare, Control, Replay, Evidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declare, the Agent Action Manifest&lt;/strong&gt;. Written once, before the agent ever runs. The manifest provides or denies access. The agent is attempting to complete a task, but the manifest keeps it in check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pull top customers&lt;/em&gt;: &lt;code&gt;default_action: allow&lt;/code&gt;. A CRM read, flagged to leave a reliance record of what data it used.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Draft a reply&lt;/em&gt;: &lt;code&gt;default_action: allow&lt;/code&gt;, review_mode: draft_first. The agent prepares content; it doesn't finish the job.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Pull contract details&lt;/em&gt;: &lt;code&gt;default_action: block&lt;/code&gt;. Off-limits for this agent, full stop.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Send the email&lt;/em&gt;: &lt;code&gt;default_action: escalate&lt;/code&gt;. Requires a named authority scope and human sign-off before it's eligible to execute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point is that the manifest lets you say &lt;em&gt;reading customer data is fine, reading contracts is not, drafting is fine, but sending the email needs a person&lt;/em&gt;: four different rules for four different actions, declared before any of them happen, instead of one blanket policy applied to "the agent" as if every action carried the same risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control, the Agent Control Plane&lt;/strong&gt;. Runs live, alongside the agent. Every time the agent tries to do something, the Control Plane checks it against what was declared, records the attempt, and either allows it, blocks it, or escalates it for approval, deterministically, every time. Every one of those outcomes gets written down, not just the bad ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replay, the Agent Replay Bundle&lt;/strong&gt;. Not a process, an export. When the run ends, everything the Control Plane recorded (what was proposed, what was decided, what was escalated, what it relied on) gets packaged into a single portable file. No more digging through the logs of a dozen systems to reconstruct one run. Every read, every draft, every escalation, everything that was allowed or held back: it's all in one place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence, the Agent Governance Evidence Pack&lt;/strong&gt;. Assembled later, across many runs. This is where you get to show your work: not "trust us, we have controls," but "here's proof: 40 sends needed sign-off this month, and here's what happened to each one."&lt;/p&gt;

&lt;h2&gt;
  
  
  Walking the scenario through the stack
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The manifest declares that sending an email requires authority and escalates rather than executing automatically, decided before the agent ever runs a single task.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mid-run, the Control Plane checks each of the agent's proposals against the manifest as they happen: pulling top customers and drafting a reply go through, pulling contract details is blocked outright, and the proposed send doesn't execute, it escalates and gets held for a person to review. Every one of those outcomes gets recorded as it happens, not just the one that got stopped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the run ends, every step (the reads, the draft, the block, the held send, and the outcome) gets packaged into a replay bundle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Over time, those bundles roll up into an evidence pack: not "we think our agent behaves," but "here's the record of every send this month that needed sign-off, and what happened to it."&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A thousand cringey emails to customers drops to zero. There are no questions about why, no furious Slack thread trying to piece it together. And if anyone does wonder, the stack's replay bundle provides the paper trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  See it yourself
&lt;/h2&gt;

&lt;p&gt;The Open Control Stack, Cognous's first release, is fully open source, and everything above (the manifest fields, the allow/block/escalate decisions, all of it) comes straight from the actual code, not a slide deck. If you want to see AI agent governance in action instead of taking our word for it, the manifest, control plane, replay bundle, and evidence pack repos are all live on &lt;a href="https://github.com/cogno-us/cognous-open-control-stack" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Clone one, run the examples, watch a proposed action get authorized, blocked, or escalated in real time. You can learn more about Cognous at &lt;a href="https://cogno.us/" rel="noopener noreferrer"&gt;cogno.us&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the first post in a series on AI agent governance. Next up, we build the manifest from this exact scenario: the real schema, the real validation rules, and what runtime authorization for AI agents actually looks like as working code. From there we'll get into the AI agent control plane that enforces it and how a single run turns into replayable AI agent evidence you can hand to an auditor. More posts coming soon.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>security</category>
    </item>
  </channel>
</rss>
