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

# Control Path

> The 6-stage pipeline that every memory operation passes through

# Control Path Pipeline

Every memory operation in Memproof -- whether it is a `remember`, `update`, `forget`, or `search` -- passes through the same deterministic pipeline. The pipeline has six stages, each emitting an immutable event to the [Audit Trail](/concepts/event-ledger).

## Pipeline Stages

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sequenceDiagram
    participant Client
    participant Orchestrator
    participant RiskEngine
    participant PolicyEngine
    participant ApprovalBroker
    participant Adapter
    participant Ledger

    Client->>Orchestrator: remember / update / forget / search
    Orchestrator->>Ledger: emit(received)
    Orchestrator->>RiskEngine: assess(operation, content, scope, context)
    RiskEngine-->>Orchestrator: RiskAssessment
    Orchestrator->>Ledger: emit(risk_assessed)
    Orchestrator->>PolicyEngine: evaluate(operation, risk, scope, context, flags)
    PolicyEngine-->>Orchestrator: PolicyDecision
    Orchestrator->>Ledger: emit(policy_decided)

    alt action = deny
        Orchestrator->>Ledger: emit(blocked)
        Orchestrator-->>Client: PolicyDeniedError
    else action = quarantine
        Orchestrator->>Ledger: emit(blocked)
        Orchestrator-->>Client: QuarantinedError
    else action = require_approval
        Orchestrator->>ApprovalBroker: request_approval(...)
        Orchestrator->>Ledger: emit(approval_requested)
        alt approved
            Orchestrator->>Adapter: create / update / delete
        else denied
            Orchestrator->>Ledger: emit(blocked)
            Orchestrator-->>Client: ApprovalDeniedError
        end
    else action = allow
        Orchestrator->>Adapter: create / update / delete
    end

    Orchestrator->>Ledger: emit(provider_attempted)
    Adapter-->>Orchestrator: result
    Orchestrator->>Ledger: emit(committed)
    Orchestrator-->>Client: MemoryOperationResponse
```

## Stage Details

<Steps>
  <Step title="received">
    The orchestrator accepts the request and generates a unique `operation_id` (format: `op-{hex16}`). An idempotency check runs first -- if the same idempotency key was already processed, the cached response is returned. The `received` event is emitted to the ledger with the operation's scope.

    **Data produced:** `operation_id`, validated request model
  </Step>

  <Step title="risk_assessed">
    The [Risk Engine](/concepts/risk-engine) scores the operation across five weighted factors: operation type, PII detection, secrets detection, source trust, and scope anomalies. A separate `assess_content_flags` call produces boolean flags (`contains_pii`, `contains_secret`) used by the policy engine.

    **Data produced:** `RiskAssessment` (score, level, factors, scorer), content flags
  </Step>

  <Step title="policy_decided">
    The [Policy Engine](/concepts/policy-engine) evaluates YAML rules in priority order against the operation type, risk assessment, scope, context, and content flags. The first matching rule wins, producing a `PolicyDecision` with one of four actions.

    **Data produced:** `PolicyDecision` (action, reason\_codes, matched\_rule\_ids, policy\_version)
  </Step>

  <Step title="approval_requested (conditional)">
    This stage only runs when the policy decision is `require_approval`. The Approval Broker sends the request to an external approval system (Attesta) or queues it for manual review. Three outcomes are possible:

    * **Synchronous approval**: the broker returns immediately and the pipeline continues.
    * **Async/pending**: the operation is stored with status `pending_approval` and the response is returned to the caller. A later call to `mp.approve()` or `mp.deny()` completes the operation.
    * **Denied**: the pipeline emits a `blocked` event and raises `ApprovalDeniedError`.
  </Step>

  <Step title="provider_attempted">
    The orchestrator delegates to the [memory adapter](/concepts/architecture) to execute the actual backend operation (`create_memory`, `update_memory`, `delete_memory`, or `search_memories`). If the adapter raises an `AdapterError`, the operation status is set to `failed` and a `ProviderUnavailableError` is raised.

    **Data produced:** `MemoryRecord` (on success)
  </Step>

  <Step title="committed / blocked">
    The terminal stage. On success, a `committed` event is emitted and the operation record is stored with status `committed`. On policy denial, quarantine, or approval rejection, a `blocked` event is emitted instead.

    **Data produced:** final `MemoryOperationResponse` or `OperationStatusResponse`
  </Step>
</Steps>

## Operation Statuses

Each operation ends in one of these statuses:

| Status             | Meaning                                       |
| ------------------ | --------------------------------------------- |
| `committed`        | Successfully persisted to the memory backend  |
| `blocked`          | Denied by policy or rejected during approval  |
| `quarantined`      | Held for review; payload stored in quarantine |
| `pending_approval` | Awaiting human or system approval             |
| `failed`           | Adapter error during provider execution       |

## Idempotency

Every mutation endpoint (`remember`, `update`, `forget`) requires an idempotency key. If you do not provide one, Memproof generates a random key. The idempotency check runs before the pipeline begins:

* **Same key, same payload**: returns the cached response.
* **Same key, different payload**: raises `ConflictError`.

```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
result = await mp.remember(
    content="user prefers dark mode",
    scope={...},
    context={...},
    idempotency_key="idem-unique-abc123",
)
```

## Approval Flow

When a policy decision returns `require_approval`, pending operations can be resolved later:

```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
# Approve a pending operation
await mp.approve(operation_id="op-abc123", actor_id="admin@acme.com")

# Or deny it
await mp.deny(operation_id="op-abc123", actor_id="admin@acme.com", notes="Not authorized")
```

<Warning>
  Operations in `pending_approval` status hold the deferred request payload in memory. Ensure approval decisions are resolved promptly to avoid unbounded memory growth.
</Warning>

## Querying Operation Status

You can check the current status of any tracked operation at any time:

```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
status = mp.get_operation_status("op-abc123")
# status.status: committed | blocked | pending_approval | quarantined | failed
# status.decision: the PolicyDecision that was made
# status.risk_assessment: the RiskAssessment produced
```
