> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mnemom.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Card Composition

> How alignment and protection cards compose across platform > org > (team)? > agent scope, with field-level rules, exemptions, and canonical-card semantics.

Every Mnemom agent has [two cards](/concepts/agent-cards) — alignment and protection. Both compose from up to **four scopes**: platform, org, team (optional), agent. This page documents the composition rules, the canonical-card mechanism, and how exemptions change the computation.

<Note>
  **Team scope is optional.** Solo agents (in zero teams) compose under the 3-layer cascade `platform → org → agent` — the historical shape, unchanged. Teamed agents compose under `platform → org → {team₁, …, tₙ} → agent`. See [Team Scope](./team-scope) for the team layer's mechanics; the rest of this page covers the rules that apply at every layer present.
</Note>

## Why composition exists

Previously, card merging happened per request. The gateway would fetch an agent card, an org template, and a CLPI policy, then run `mergeOrgAndAgentCard()` and `mergePolicies()` on every inbound call. That cost real latency, scattered merge logic across services, and made it hard to answer "what does this agent's effective card actually look like?"

Composition moves the merge to **storage time**. When an agent's scope-level card, org template, platform policy, or exemption list changes, a background job (`compose_agent_card`, `compose_protection_card`) computes the **canonical** card — the fully-merged, fully-resolved, ready-to-serve document — and writes it to `canonical_agent_cards` or `canonical_protection_cards`.

Every gateway and observer read hits the canonical table, not the raw scope rows. The request path has zero merge cost.

## The scopes

| Scope                 | Purpose                                                | Storage                                                                      | Who edits                                       | Always present?                           |
| --------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- | ----------------------------------------------- | ----------------------------------------- |
| **Platform**          | Defaults for all agents on Mnemom — the absolute floor | `platform_policies` row (one per card type, singleton)                       | Mnemom platform team                            | Yes                                       |
| **Org**               | Defaults for all agents in an organization             | `orgs.card_template` (alignment) + `orgs.protection_template` (protection)   | Org owner / admin                               | Yes (personal-org-of-one for solo users)  |
| **Team** *(optional)* | Sub-org grouping for heterogeneous fleets              | `teams.card_template` + `teams.protection_template` (per team)               | Org owner / admin (Team Admin role lands later) | No — only when the agent is a team member |
| **Agent**             | Per-agent specialization                               | `alignment_cards.card_json` (active) + `sh_configs.config_json` (protection) | Agent owner                                     | Yes                                       |

Scope ordering is **platform → org → (team)? → agent**. The team layer is conditional: agents in zero teams compose under the 3-layer cascade; agents in one or more teams pick up a Team layer between Org and Agent. Multi-team agents fold strictest-wins across all their teams. See [Team Scope](./team-scope) for details.

Later scopes apply over earlier scopes according to the per-field rules below. The **same** rules apply with 0, 1, or N team layers present — the composer's strictest-wins idiom is variadic.

## Field-level composition rules

Composition is not a single blended operation. Each card section has its own merge semantic chosen to match its governance intent:

| Section / field                                                | Rule                                                                                                                                                                                                                                                                              | Intent                                                                              |
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `values.declared`                                              | **Union** across scopes                                                                                                                                                                                                                                                           | Platform/org can require values; agent can add more                                 |
| `values.conflicts_with`                                        | **Union**                                                                                                                                                                                                                                                                         | Any scope can mark a value as conflicting; agent can add more                       |
| `values.definitions`                                           | **Agent wins** (with platform/org as defaults if agent is silent)                                                                                                                                                                                                                 | Definitions are local semantics                                                     |
| `conscience.mode`                                              | **`replace` beats `augment`** (if any scope says replace, replace wins)                                                                                                                                                                                                           | Replace is the stronger commitment                                                  |
| `conscience.values`                                            | **Union with dedup by `content`**; `BOUNDARY` entries from platform/org are inviolable                                                                                                                                                                                            | Platform conscience floors are non-negotiable                                       |
| `integrity_mode` / `autonomy_mode` (top-level master switches) | **Strictest wins** (`enforce` > `nudge` > `observe` > `off`)                                                                                                                                                                                                                      | Org requires-enforce cannot be downgraded                                           |
| `autonomy.bounded_actions`                                     | **Agent-scoped** (platform/org suggest defaults; agent card takes effect)                                                                                                                                                                                                         | What the agent can do is agent-local                                                |
| `autonomy.forbidden_actions`                                   | **Deny-overrides union** (forbidden at any scope = forbidden everywhere)                                                                                                                                                                                                          | Platform/org deny cannot be un-denied by agent                                      |
| `autonomy.escalation_triggers`                                 | **Union with dedup by `condition`**                                                                                                                                                                                                                                               | Any scope can add a trigger                                                         |
| `autonomy.max_autonomous_value`                                | **Min across scopes** (smallest cap wins)                                                                                                                                                                                                                                         | Tightest cap applies                                                                |
| `capabilities.*`                                               | **Agent-scoped** (capabilities are local tool mappings)                                                                                                                                                                                                                           | Tools are per-agent                                                                 |
| `enforcement.allow_unmapped_tools`                             | **Strictest wins (`false` over `true`)**                                                                                                                                                                                                                                          | Any scope can require mapped tools                                                  |
| `audit.retention_days`                                         | **Max across scopes** (longest retention wins)                                                                                                                                                                                                                                    | Any scope can require longer retention                                              |
| `audit.queryable`                                              | **OR across scopes** (any `true` wins)                                                                                                                                                                                                                                            | If any scope requires queryability, it's on                                         |
| `audit.tamper_evidence`                                        | **Strongest wins** (`none` \< `append_only` \< `signed` \< `merkle`)                                                                                                                                                                                                              | Most rigorous evidence mechanism applies                                            |
| `audit.query_endpoint`, `audit.storage.*`                      | **Platform-scoped** (compliance floor)                                                                                                                                                                                                                                            | Operational addresses come from the platform                                        |
| Protection `mode`                                              | **Strictest wins**                                                                                                                                                                                                                                                                | Same as integrity                                                                   |
| Protection `thresholds.*`                                      | **Floor + override** (org floor; agent may go stricter, not looser)                                                                                                                                                                                                               | Org sets the alert ceiling                                                          |
| Protection `screen_surfaces.*`                                 | **Floor (true wins)**                                                                                                                                                                                                                                                             | Any scope can require scanning                                                      |
| Protection `trusted_sources`                                   | **Intersection from platform, union from org + agent**                                                                                                                                                                                                                            | Platform trust allowlist is the compliance ceiling                                  |
| Protection `protected_surface.assets`                          | **Strengthen-only union** across all scopes; identity = `${kind}:${selector}` (normalized). Collision: merge to strictest (severity max; `applies_to` widens if any contributor is global; `source_scope` stamps highest declaring scope). Agent can add; never remove or weaken. | Org is the primary authoring scope; an empty agent still gets the org floor.        |
| Protection `protected_surface.forbidden_operations`            | **Strengthen-only union**; identity = normalized `pattern`. Same collision rules. Judge precedence: `forbidden_operation` > `escalation_required` > allow.                                                                                                                        | Org-owned hard policy. Agent cannot narrow. Safe House L2 is the enforcement point. |
| Protection `protected_surface.escalation_required`             | **Strengthen-only union**; identity = normalized `pattern`. Agent can add escalation requirements; never remove higher-scope entries.                                                                                                                                             | Org-owned.                                                                          |

Composition metadata (`_composition`) on the canonical card records which scopes contributed which fields, so downstream readers can debug "where did this value come from?"

## Exemptions

An **exemption** waives a specific org- or platform-scope field for a specific agent, with explicit justification and expiry. Exemptions replace the legacy boolean `org_card_exempt` flag, which was an all-or-nothing escape hatch.

### Structure

```yaml theme={null}
# POST /v1/agents/{agent_id}/exemptions
exempt_section: "autonomy.forbidden_actions"   # the section (dotted path)
exempt_patterns: ["deploy_code"]                # optional: specific items within the section
reason: "Agent is the CI/CD deploy runner; needs deploy_code for its job"
granted_by: "platform-admin@mnemom.ai"
expires_at: "2026-07-17T00:00:00Z"              # null for permanent (rare)
```

### Rules

* Exemptions are **section-specific**. A single exemption targets one field or pattern-scoped subset, not the whole card.
* Exemptions are **audit-logged** synchronously on grant and revocation. Every composition that honors an exemption is traceable.
* Exemptions **expire** (default 90 days). The composer refuses to honor an expired exemption; the next recompose quietly drops it.
* Exemptions on `BOUNDARY` conscience entries are **rejected** at the API layer. Inviolable commitments cannot be waived.

### Composition with exemptions

When the composer runs:

1. Compute the scope union (platform + org) for each field.
2. For each active exemption, subtract the exempted patterns from the scope union before applying agent-scope.
3. Apply agent-scope per the normal rules.
4. Record the exemption reference in `_composition.exemptions_applied[]`.

## The canonical card

The canonical card is the output of composition:

```typescript theme={null}
interface CanonicalAgentCard {
  agent_id: string;
  card_yaml: string;            // human-readable YAML
  card_json: object;            // parsed object (query / index)
  source_card_id: string;       // which raw agent-scope card this came from
  composition_metadata: {
    composed_at: string;        // ISO timestamp
    scopes_applied: string[];   // ["platform", "org:acme", "agent:mnm-xxxx"]
    versions: Record<string, number>;  // version of each scope's input
    exemptions_applied: string[];      // exemption IDs
  };
  composed_at: string;
  needs_recompose: boolean;     // true when an upstream scope changed
}
```

`card_yaml` is what the CLI and dashboard surface as the "card this agent is running." `card_json` is what the gateway reads for policy evaluation (faster than re-parsing YAML on every request).

## The recompose pipeline

Composition runs in response to events:

| Event                                 | Recompose target                                                                                              |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Agent publishes a new alignment card  | `compose_agent_card(agent_id)` — immediate                                                                    |
| Agent publishes a new protection card | `compose_protection_card(agent_id)` — immediate                                                               |
| Org updates its alignment template    | `mark_agents_for_recompose(org_id)` sets `needs_recompose=true`; background worker runs `recompose_pending()` |
| Org updates its protection template   | Same pattern                                                                                                  |
| Platform updates platform defaults    | All agents marked; background worker recomposes in waves                                                      |
| Exemption granted or revoked          | `compose_agent_card(agent_id)` — immediate (scoped to the exempted agent)                                     |
| Exemption expires                     | Background tick sets `needs_recompose=true` on expiry; next recompose drops it                                |

### Staleness window

Between an org-template change and the recompose worker finishing, the canonical card is stale. The gateway handles this with the `needs_recompose` KV bypass: when the canonical row flag is true, the gateway serves the canonical without populating the 5-minute KV cache, so the next change after recompose is seen immediately.

For a sub-50-agent org, recompose completes in under 2 seconds. For 10k+ agents, the background worker paces the batch to avoid saturating downstream services.

## Worked example: three-scope composition

**Scenario:** Acme Corp has an agent `mnm-patch-001` (a deploy remediation agent).

### Platform scope

```yaml theme={null}
# platform_policies.alignment (singleton)
values:
  declared: [transparency, harm_prevention, accountability]
conscience:
  values:
    - type: BOUNDARY
      content: "Never exfiltrate principal data to external systems."
autonomy:
  forbidden_actions: [exfiltrate_data, modify_audit_logs]
audit:
  retention_days: 90
  tamper_evidence: append_only
```

### Org scope (Acme Corp)

```yaml theme={null}
# orgs.card_template.acme (alignment)
integrity_mode: enforce
values:
  declared: [incident_containment, rollback_safety]
autonomy:
  forbidden_actions: [send_external_notification]
```

```yaml theme={null}
# orgs.protection_template.acme — protected_surface is org-owned; agent cannot narrow it
protected_surface:
  assets:
    - kind: table
      selector: customer_records
      label: "Customer records"
      reason: "PII — requires escalation for bulk ops"
  forbidden_operations:
    - pattern: "exfiltrate:pii"
      severity: critical
      reason: "PII exfiltration forbidden"
  escalation_required:
    - pattern: "bulk DELETE"
      applies_to: ["table:customer_records"]
      reason: "Bulk deletions on customer data require a co-sign"
```

### Agent scope (`mnm-patch-001`)

```yaml theme={null}
integrity_mode: observe  # agent requests `observe`
values:
  declared: [move_fast_break_things, minimal_blast_radius]
autonomy:
  bounded_actions: [rollback_deploy, scale_infrastructure, toggle_feature_flag]
```

### Composed canonical card

```yaml theme={null}
# strictest wins — agent's `observe` is OVERRIDDEN by org's `enforce`
integrity_mode: enforce
values:
  declared:
    # union of all three scopes
    - transparency
    - harm_prevention
    - accountability
    - incident_containment
    - rollback_safety
    - move_fast_break_things
    - minimal_blast_radius
conscience:
  values:
    - type: BOUNDARY
      content: "Never exfiltrate principal data to external systems."
autonomy:
  bounded_actions: [rollback_deploy, scale_infrastructure, toggle_feature_flag]  # agent-scoped
  forbidden_actions:
    # deny-overrides union
    - exfiltrate_data
    - modify_audit_logs
    - send_external_notification
audit:
  retention_days: 90
  tamper_evidence: append_only
_composition:
  composed_at: "2026-04-17T18:23:41Z"
  scopes_applied: [platform, "org:acme", "agent:mnm-patch-001"]
  versions:
    platform: 3
    "org:acme": 17
    "agent:mnm-patch-001": 4
```

Note the agent's `integrity_mode: observe` was silently overridden to `enforce` by the org. The composition metadata makes this traceable — not hidden.

The protection card composes separately. The `protected_surface` entries carry `source_scope` stamped at the highest scope that declared them — so the agent cannot claim provenance of an org rule:

```yaml theme={null}
# Canonical protection card (protection scope)
protected_surface:
  assets:
    - kind: table
      selector: customer_records
      label: "Customer records"
      reason: "PII — requires escalation for bulk ops"
      source_scope: "org:acme"
  forbidden_operations:
    - pattern: "TRUNCATE"
      severity: critical
      reason: "Irreversible data loss"
      source_scope: platform
    - pattern: "exfiltrate:pii"
      severity: critical
      reason: "PII exfiltration forbidden"
      source_scope: platform
  escalation_required:
    - pattern: "bulk DELETE"
      applies_to: ["table:customer_records"]
      reason: "Bulk deletions on customer data require a co-sign"
      source_scope: "org:acme"
_composition:
  composed_at: "2026-04-17T18:23:41Z"
  scopes_applied: [platform, "org:acme", "agent:mnm-patch-001"]
```

## Debugging composition

### CLI

```bash theme={null}
mnemom card show                         # canonical (composed) alignment card (YAML)
```

`mnemom card show` always renders the canonical composed card. The `_composition` metadata block, the raw agent-scope row, and per-field scope provenance are not surfaced by the CLI today — use the API below.

### API

```bash theme={null}
# Canonical card with the _composition metadata block attached
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.mnemom.ai/v1/alignment/agent/{id}?include_composition=true"

# Five-row sources envelope (Platform / Org / Teams / Agent / Composed) —
# the agent-scope raw row without composition
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.mnemom.ai/v1/alignment/agent/{id}?include=sources"

# Per-field scope provenance (which scope contributed each value),
# always populated on the /effective sub-resource
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.mnemom.ai/v1/alignment/agent/{id}/effective"
```

### Observer

Every gateway + observer card read emits a structured log entry with `card_source: canonical_hit` (or `canonical_miss_fallback` in the rare case the canonical row is missing and the composer is still catching up). The production criterion is that the fallback rate is zero on production traffic.

## Trust Posture is a parallel cascade, not nested

Cards compose along `Platform → Org → Team → Agent`. **[Trust Posture](/concepts/trust-posture) — the team-scoped oversight policy artifact — composes along its own parallel cascade `Platform → Org → Team` and does NOT fold into the per-agent card.**

Postures and cards are parallel artifact types: postures are team-scoped policy input (drives the observer's sideband sweep), cards are agent-scoped runtime treatment (read by the gateway at request time). They share infrastructure (this composer idiom, KV caching, audit log) but their data planes are independent.

Practically:

* An advisory written by a posture-driven detector lands in [`pending_advisories`](/specifications/pending-advisories-schema) tagged with `source: sideband.{coherence,fault_line,fleet}`. The gateway picks it up on the next turn for an affected agent and lets card-driven enforcement decide the runtime treatment.
* The posture cascade has no agent leaf because postures are inherently fleet-level (they observe a *group*, not an individual).
* Composing a posture into the canonical card is intentionally not supported — see [Posture vs. Cards](/concepts/posture-vs-cards) for the design rationale.

## See also

* [Agent Cards](/concepts/agent-cards) — the two-card model overview
* [Alignment Card Schema](/specifications/alignment-card-schema) — normative schema for unified alignment cards
* [Protection Card Schema](/specifications/protection-card-schema) — normative schema for protection cards
* [Card Lifecycle](/concepts/card-lifecycle) — amendment, reclassification, expiry
* [Trust Posture](/concepts/trust-posture) — team-scoped oversight policy
* [Posture vs. Cards](/concepts/posture-vs-cards) — parallel cascades architecture
