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. In our first post, Cognous Keeps Your AI in Check https://dev.to/cognous/cognous-keeps-your-ai-in-check-69l , 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 https://github.com/cogno-us/cognous-open-control-stack . 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 https://github.com/cogno-us/cognous-open-control-stack 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: bash $ 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: bash $ 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 https://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.