Skip to content
Back to Blog
Trust, audit, governance
March 14, 2026
·by ·8 min read

Approval-Mode Tiers: A Compatibility Taxonomy You Can Actually Ship

Share:XBSMRedditHNEmail
Approval-Mode Tiers: A Compatibility Taxonomy You Can Actually Ship illustration

A team I work with shipped an AI assistant for their support org last summer. The pilot went well. The second team adopted it in October. By December the slack channel was an anthropology project: someone needed an approval step before the agent emailed customers, someone else needed one before refunds, a third before file uploads, a fourth before contract changes — but only above a threshold, and only outside business hours, and only for VIP accounts.

By March they had thirty named approval gates. Pull requests to add the thirty-first were the team’s most-disliked Jira ticket. Audit reports took a week. Every new use case made the problem worse, not better.

This is not only a tooling problem. It is a classification problem. The team was binding approvals to workflows when it needed stable operational labels plus a native description of each action’s effect, authority, reversibility, interaction boundary, and data scope.

2026 update: tiers are the v1 projection, not the native risk model

The five approval modes remain required in v1 schemas, adapter manifests, compiled tool surfaces, critic verdicts, gateway envelopes, approval events, and Decision Records. But they are a compatibility projection and safety ceiling—not ContextOS’s complete risk model. network describes an effect boundary, delegated describes authority, and destructive describes reversibility; treating them as one universal order hides important combinations.

New implementations declare native ActionRisk across five independent dimensions and derive a conservative v1 mode when an older consumer requires one. A label that exists only in policy prose will drift. A vector and projection emitted into the contract can be enforced, queried, replayed, and reviewed.

The five v1 compatibility tiers

ContextOS v1 uses exactly five approval-mode values. Every adapter capability declares the highest compatibility mode it can produce; policy can select a lower effective mode within its priority but cannot exceed that ceiling. Native policy still evaluates every ActionRisk dimension independently.

ModeExamplesDefault policy
read_onlylookups, search, retrievalallow with audit
local_writetenant-scoped writes that can be reverted in-tenant (notes, drafts, memory)allow with idempotency key + audit
networkoutbound calls, webhooks, third-party readsallow with egress allow-list + rate budget
delegatedacts on behalf of a user against an external system (booking, message send, calendar write)require valid user delegation token + per-call evidence
destructiveirreversible side effects (payment capture, account deletion, data export)require named approver + frozen evidence snapshot + post-execution audit

Five wire-compatible labels, not fifty workflow-specific gates. The constraint makes v1 operations comparable; the native vector preserves the risk information the ladder cannot express.

Where the tier is enforced

The tier is not just metadata for the UI. It is checked at each place where risk can change shape.

Runtime pointWhat it checksArtifact emitted
Adapter registrationCapability declares native ActionRisk, its maximum v1 approval_mode, and constraintsVersioned manifest entry; signing is an optional provenance profile
Context compileCompiler surfaces only tools allowed by pack, policy, and RunContext.safety_modeCompiledContext.tool_manifest
Critic verifyProposed plan stays within surfaced tools, argument bounds, and effective modecritic_verdict
Tool GatewayExecute-time policy re-check, credential scope, idempotency, and egresstoolCall / toolResult
Approval gateHuman or delegated authorization binds to frozen evidenceapproval_event
Decision RecordFinal action records declared mode, effective mode, policy rule, and approverDecisionRecord.approvals[]

Why workflow-named gates rot

When you name gates after workflows, three things happen. They are obvious in retrospect; they are very hard to see in the moment.

First, the taxonomy explodes. Every new use case wants a name, every name acquires a special case, and within a year you have GATE_REFUND_FRAUD_VIP_TIER2_HOLIDAY_OVERRIDE. Auditors do not query that schema; they archaeologize it.

Second, the same business action carries different risk in different contexts, and a single gate name flattens that risk. A 50-rupee refund and a 5-million-rupee refund are not the same risk event. The team that named GATE_REFUND_APPROVAL had to either accept that ergonomic mistake or invent yet another gate to fix it. They invented one.

Third, cross-workflow comparison is impossible. “Show me every destructive action this quarter, by approver, ranked by override rate” is one line of SQL against a tier taxonomy. It is a research project against a workflow-name taxonomy. The compliance org rapidly learns to stop asking.

A stable compatibility taxonomy improves all three because gate names are runtime artifacts that bind to a mode. You can still keep gate names for routing—GATE_FINANCE_APPROVAL is fine as a runtime artifact—but the audit story queries both the native vector and the projected mode.

The downgrade-only invariant

The single most important v1 rule is that policy bundles can select a lower effective approval mode but cannot exceed the wire-time ceiling. This rule never permits policy to weaken a native ActionRisk constraint.

Imagine the alternative. A buggy or compromised policy bundle could escalate any read into a destructive action. The blast radius of a runtime mistake would scale with the number of bundles you operate. With downgrade-only, the worst a runtime mistake can do is under-protect a call — never over-empower one. That is the difference between “we can recover” and “we have an incident.”

The wire-time contract on the adapter pins the v1 ceiling. A capability for issuing refunds projects to destructive in its registration. The runtime can select a lower mode for a tightly bounded VIP refund under a recorded rule, but cannot exceed the ceiling or bypass the vector’s authority, data, destination, reversibility, and evidence constraints. Even a fully compromised model cannot expand those constraints.

A walkthrough

The intent is support.refund. The wire-time facts include a native vector and a conservative destructive compatibility projection:

{
  "permission_id": "perm_payments_refund_capped",
  "adapter_id": "adp_payments",
  "capability": "issue_refund",
  "action_risk": {
    "effect": "external_state",
    "authority": "human_approved",
    "reversibility": "compensatable",
    "interaction": "api",
    "data_scope": "CONFIDENTIAL"
  },
  "approval_mode": "destructive",
  "arg_constraints": {
    "amount_inr": { "min": 1, "max": 50000 },
    "currency": { "enum": ["INR"] },
    "idempotency_key": { "required": true, "pattern": "^ik_[a-z0-9]{16}$" }
  }
}

A VIP-instant-refund rule downgrades it within bundle priority for low amounts:

{
  "rule_id": "R_VIP_INSTANT_REFUND",
  "applies_to": { "intent": "support.refund" },
  "if": {
    "and": [
      { "==": [{ "var": "request.context.user.is_vip" }, true] },
      { "<=": [{ "var": "request.context.refund_amount" }, 200] }
    ]
  },
  "then": { "allow": true, "approval_mode": "delegated", "requires_approval_gate": null }
}

For a 200-rupee refund to a VIP, the effective mode is delegated. No human gate, but the Decision Record still names the rule that produced the downgrade and the evidence that satisfied it. If an auditor asks why a destructive capability auto-executed, the answer takes seconds: the rule, the evidence, the bundle priority.

For a 50,000-rupee refund the rule would not match, the wire-time destructive would stand, and execution would route through propose → approve → execute with a frozen evidence snapshot. Same capability, different effective mode, complete audit either way.

What changes operationally

Once the vector and projection are consistently recorded, the dashboard becomes comparable across workflows. Compliance rate, gate honor rate, latency-to-approval, and override rate can be sliced by native dimension and compatibility mode. “Destructive-mode share by tenant” remains useful, but it should sit beside irreversible-effect, delegated-authority, external-state, restricted-data, and interaction-boundary views.

Two metrics worth watching specifically:

  • Cross-tier downgrade rate. High values are not necessarily bad — VIP rules and small-amount carve-outs are legitimate downgrades. They are, however, a leading indicator of misclassified wire-time. If 80% of destructive calls get downgraded, the wire-time tier is probably wrong.
  • Approver review time per evidence_snapshot_hash. If the median approver spends three seconds on a frozen snapshot, the gate has become a rubber stamp; either the threshold needs to move down, or the snapshot needs more context.

Reviewer checklist

Before a new capability ships, the reviewer should be able to answer this without reading the prompt:

QuestionBlock if
What is the native action risk and wire-time maximum mode?The adapter registration is missing either the vector or its conservative v1 projection.
Can policy only constrain the capability?A rule can make the call more powerful than its manifest ceiling.
Are arguments bounded?A side-effecting capability lacks schema limits, idempotency, or destination constraints.
Is the evidence snapshot frozen before approval?The approver sees live, mutable data instead of a hash-addressed snapshot.
Can audit query by tier across workflows?The only searchable concept is a workflow-specific gate name.

What people get wrong

The two arguments against this model are both wrong, and they are wrong for the same reason.

The first is: “five tiers are not enough to describe risk.” That is correct, which is why ActionRisk is multidimensional. Five labels are enough for the v1 compatibility surface because workflow-specific gate names remain routing artifacts rather than new modes.

The second is: “this adds latency.” It does not — most calls are read_only or local_write and never see a gate. The gates kick in only for calls that should pay the latency. If your destructive-mode share is non-trivial, your problem is the workflow, not the taxonomy.

The operational lesson is that stable labels let engineering ship without inventing and auditing a new mode for every gate. Native dimensions then give security and operations the precision those labels intentionally lack.

A closing thought

Risk taxonomies are unglamorous. They get out-voted by “let’s just add a gate.” Six months later, the team that resisted the taxonomy has the worst audit experience in the company.

If you want governance you can ship, declare ActionRisk for every capability, derive one of the five v1 compatibility modes, and bind workflow gates to policy rather than inventing new modes. Let policy select a lower mode only inside the declared ceiling and never let that projection bypass native constraints. The audit reports and cross-workflow views then have both compatibility and precision.

Found this useful? Share it.

Share:XBSMRedditHNEmail

Continue through the same topic without returning to the index.

View the series