cd /news/ai-agents/the-manifest-that-keeps-your-ai-agen… Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-93836] src=dev.to β†— pub= topic=ai-agents verified=true sentiment=Β· neutral

The Manifest That Keeps Your AI Agent Honest

Cognous has released the Open Control Stack, a set of four open-source projects designed to keep AI agents in check by enforcing pre-defined permissions. The first component, Declare, introduces the Agent Action Manifest, a versioned file that outlines an agent's allowed actions, tools, and data classifications. This manifest aims to prevent scenarios like an AI customer-service agent sending mass emails without approval.

read9 min views1 publishedAug 12, 2026

In our first post, Cognous Keeps Your AI in Check, 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.

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.

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 Agent Action Manifest. This post describes the Action Manifest tooling in the Open Control Stack, available on GitHub.

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:

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

(Field names here follow the public schema β€” check the repo for the exact current shape if you're implementing against it.)

Rules are only as good as what is defined. At the very top of the manifest, we explicitly cover anything not mentioned: "default_action": "block"

. So when a new tool is onboarded (for example send_tweet

), you don't need to go back and update every manifest β€” it's blocked until you expressly give the agent permission to use it.

For those actions that are defined:

pull_top_customers

and draft_reply

are both allow

, but not the same kind of allow: pulling the top customers is allowed, full stop, but the draft reply has review_requirement.mode: draft_first

β€” the agent can write it, but it stops there.send_email

isn't just "escalate" as a vague gesture β€” it names the exact authority scope required (email.send.customer

), the exact reviewer role (support-lead

), and which payload fields have to be redacted before this record goes anywhere near an audit export.pull_contract_details

is the odd one out β€” it doesn't strictly need to be there. default_action: block

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 restricted

, is a documented decision someone can point to. A blocked action that's simply never mentioned looks identical to an oversight.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 should happen β€” before the agent has run even once.

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:

Action Meaning
read
retrieves or inspects information
write
creates or modifies information, not necessarily sent externally
external_send
sends information outside the system boundary
delete
removes information or records
export
packages or transfers information downstream
purchase
initiates or prepares a purchase
approve
approves or authorizes a workflow step
escalate
routes to a human or supervisory process
other
anything not otherwise classified

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.

Action type says what kind of thing an action is. Review mode says who has to look at it, and when β€” and it's doing just as much governance work in our example as action type is. There are four:

Mode Meaning
none
no specific review posture declared
human_review
a human is expected to look at it before execution or completion β€” no specific person named
approval_required
approval is required from a named reviewer role
draft_first
the agent produces a draft; it does not finish the action itself

The difference between human_review

and approval_required

is easy to miss but it matters: human_review

says "someone should look at this" without saying who. approval_required

is stricter β€” it names a reviewer role, and the validator enforces that: an approval_required

action with no reviewer_role

fails validation outright.

That's why send_email

in our manifest uses approval_required

with reviewer_role: "support-lead"

, not human_review

. Sending customer email isn't "someone should probably glance at this" β€” it's "this specific role signs off, or it doesn't go out."

Worth being precise about what "support-lead"

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."

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

A reliance_requirement

declares that an action should leave a record of its source β€” a tool, a database, a file, an API, or user input. pull_top_customers

declares reliance on tool

and database

, because a governance review should be able to see it actually came from the CRM and not somewhere the agent invented. send_email

declares reliance on tool

and user_input

, tying the send back to the approved draft it was built from.

draft_reply

and pull_contract_details

don't declare a reliance requirement β€” which is why the summarize output above shows Actions requiring reliance: 2

, not 4.

A manifest can be valid JSON, but be semantic nonsense. A delete

action on the production database marked allow

isn't caught by JSON Schema. It's caught by the validator, which checks things like:

external_send

, write

, delete

, purchase

, approve

) missing an authority requirement it should have?approval_required

but missing a reviewer role?Run it from the CLI β€” aam

, 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:

$ aam validate customer-service-agent-manifest.json
Validation result: VALID
Manifest ID:       customer-service-agent-manifest

Warnings (3):
  [W004] [actions[1](draft_reply)] Action 'draft_reply' has effective default_action 'allow' for action_type 'write'.
  [W006] [actions[1](draft_reply).payload_policy] Action 'draft_reply' has action_type 'write' but no payload_policy is declared.
  [W005] [actions[2](pull_contract_details).reliance_requirement] Action 'pull_contract_details' has action_type 'read' but no reliance_requirement is declared.

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.

Next we can summarize the manifest with the summarize command:

$ 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 type:
  external_send: 1
  read: 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

That "2" next to authority is easy to misread as just the send β€” it's actually draft_reply

and send_email

both. Drafting on a customer's behalf declares its own authority requirement (email.draft.customer

), separate from the one on the final send (email.send.customer

). 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.

aam

verifies that your manifest is ready, and gives the team a concise summary of what's allowed.

The manifest is the Declare layer, and it stays in its lane:

email.send.customer

being 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.

The manifest says what the agent may propose. It says nothing about what the agent actually does on a Tuesday afternoon when it's mid-run and reaching for the send button. That's the Agent Control Plane β€” 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.

The manifest repo, examples, schemas, and test suite are live now: github.com/cogno-us/cognous-open-control-stack. Clone it, validate the customer-service example, break it on purpose and watch the validator catch you.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @cognous 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/the-manifest-that-ke…] indexed:0 read:9min 2026-08-12 Β· β€”