# The Manifest That Keeps Your AI Agent Honest

> Source: <https://dev.to/cognous/the-manifest-that-keeps-your-ai-agent-honest-3e97>
> Published: 2026-08-12 14:50:04+00:00

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.
