# Region-pinned agent (/docs/region-pinned-agent)



A region-pinned agent is the shape an enterprise reaches for when
the AI program is already in production and the question stops
being "does it work" and starts being "is the inference auditable
under the contract." Region of execution is fixed per call class.
The model family is fixed per call class. Failover stays inside
the vetted list. A parity-validation suite proves the substitute
behaves the same as the primary.

This page walks the four pieces a procurement reviewer asks
about: region and family pinning at session start, parity
validation across the failover list, attestation of which model
served which call, and the audit row a regulator reads.

**Related shapes.**
[Regulated-domain agent](/docs/regulated-domain-agent) if the
domain itself (HIPAA, FedRAMP, PCI, 21 CFR Part 11) drives the
constraint.
[Multi-tenant SaaS agent](/docs/multi-tenant-saas-agent) if one
runtime serves many region-pinned customers.
[Cloud-routed agent](/docs/cloud-routed-agent) if the inference
runs through AWS Bedrock, Azure OpenAI Service, or GCP Vertex AI
rather than the lab's direct API.

## What you're building [#what-youre-building]

A turn loop whose routing decisions are pre-declared, not
discovered at runtime. The shape is the same regardless of which
lab is the primary:

* A session locks one family per call class — utility, reasoning,
  converse, synthesize each pin to a vetted family at construction.
* `permittedFamilies` constrains the cascade to the families the
  contract clears. Out-of-list providers can't be reached even as
  a last-resort fallback.
* A parity-validation suite runs the substitute family against
  the primary on a held-out fixture set. The diff is what proves
  the failover is safe to take.
* Every call writes one ledger row carrying the family, the
  model, the call class, and the region.

## Lock the permitted family set at session start [#lock-the-permitted-family-set-at-session-start]

`permittedFamilies` locks the set of provider families a session
can ever reach. The cascade walks rungs inside that set only;
which family serves each call class is decided by the model-family
matrix (family × callClass), never widened past the locked set.

```typescript
// lib/runtime.ts
import { SessionRuntime, definePleachPlugin } from "@pleach/core";

export function buildPinnedRuntime(req: AuthedRequest) {
  return new SessionRuntime({
    provider:     anthropicProvider,
    storage:      new SupabaseAdapter({ client: supabase }),
    checkpointer: new SupabaseSaver({ client: supabase }),
    plugins:      [definePleachPlugin("cleared-tools", { tools: clearedTools })],
    // The reachable family set is locked at session start. Which family
    // serves each call class (synthesize / reasoning / converse / utility)
    // is governed by the model-family matrix (family × callClass), not a
    // per-session field — the cascade only ever walks families in this set.
    permittedFamilies: new Set(["anthropic", "openai"]),
    // Residency is a hard constraint: an out-of-region family can't be reached.
    permittedRegions:  new Set([req.region]),
    tenantId:          req.tenantId,
  });
}
```

The lock is structural, not advisory. An out-of-list family can't
be reached under any cascade condition. The permitted set is
recorded in the session row — an auditor reading the contract
clause and the session row sees the same family list.

See [Family lock](/docs/family-lock) for the cascade rules and
[Call classes](/docs/call-classes) for what each class covers.

## Parity validation across the failover list [#parity-validation-across-the-failover-list]

A failover claim is unverified until the substitute behaves the
same as the primary on a held-out fixture set. `@pleach/eval`
records a fixture run against the primary, then replays it
against each family in the permitted list.

```typescript
import { createReplayRuntime } from "@pleach/replay";

for (const family of ["anthropic", "openai"]) {
  const challenger = buildPinnedRuntime({
    ...req,
    overrideCallClassFamilies: { synthesize: family },
  });

  const replayRuntime = createReplayRuntime({
    sessionRuntime: challenger,
    tenantId:       req.tenantId,
  });

  const replay = await replayRuntime.replayTurn({
    chatId:    sessionId,
    tenantId:  req.tenantId,
    messageId: goldenMessageId,
  });

  // `replay.state` is the `HydratedHarnessState` projection the turn
  // closed on; `replay.sequenceNumberRange` bounds the event window
  // walked. There is no built-in `.diff` field — compare the tool-call
  // set / refusal pattern / citation count across families from the
  // returned state yourself.
  console.log(family, replay.state, replay.sequenceNumberRange);
}
```

The replayed state is what procurement reviews. A failover that changes
the refusal pattern or the tool-call set isn't safe to take silently.
(For tamper-evidence rather than parity, `@pleach/replay` also ships a
hash-chain `verifyIntegrity` verdict — `{ valid, brokenAt? }` — that
proves an event log wasn't altered between record and replay.)

## What today ships [#what-today-ships]

Today, `@pleach/core` ships the family-strict cascade,
`permittedFamilies`, deterministic replay, and the audit row
that records which family served each call. `@pleach/eval`
ships the recording + replay engine that drives the parity
suite.

## Roadmap [#roadmap]

Roadmap pieces, in expected order:

* **Per-call-class family pinning.** Today `permittedFamilies`
  locks the reachable family *set*; the underlying matrix already
  keys on `(family, callClass)`, but a per-session surface that
  pins a specific family to each call class is still being
  widened.
* **Cascade-time region enforcement.** `permittedRegions` is a
  real session field today, enforced at the lock-time guard
  (`isRegionPermitted`). Cascade-time enforcement — failing closed
  on an out-of-region resolution mid-cascade — lands when the
  matrix grows a region-keyed dimension. Until then region is a
  lock-time constraint, not an in-cascade one.
* **Article 12 attestation pack.** A signed manifest of which
  family + model + region served each turn, in a shape an EU AI
  Act reviewer can verify offline. The audit row carries the
  underlying data today; the signed-attestation surface ships
  later.
* **Parity-suite scaffold.** The replay engine handles the
  per-turn diff today; a multi-fixture parity suite that
  aggregates pass/fail across a corpus and reports per-family
  divergence is a planned `@pleach/eval` addition.

No dates. Track the upstream package READMEs and CHANGELOG for
landing notices.

## What the audit row carries [#what-the-audit-row-carries]

Every row in `harness_auditable_calls` carries the fields a
procurement reviewer reads first:

| Column       | Carries                    | Reviewer question it answers              |
| ------------ | -------------------------- | ----------------------------------------- |
| `record_id`  | ULID, monotonic            | "is the chain intact?"                    |
| `tenant_id`  | runtime context            | "which business unit?"                    |
| `turn_id`    | one per user message       | "what was the unit of work?"              |
| `call_class` | utility / reasoning / etc. | "which class served this?"                |
| `family`     | resolved family            | "did routing stay in the contract list?"  |
| `model_id`   | resolved at call time      | "which model produced this?"              |
| `region`     | runtime context            | "did the call run in the cleared region?" |
| `created_at` | server clock               | "when, to the millisecond?"               |

See [Auditable call row](/docs/auditable-call-row) for the full
column list.

## Where to go next [#where-to-go-next]

<Cards>
  <Card title="Family lock" href="/docs/family-lock" description="The permittedFamilies constraint and the cascade rules it constrains." />

  <Card title="Call classes" href="/docs/call-classes" description="utility, reasoning, converse, synthesize — the four classes a per-class pin keys on." />

  <Card title="Model resolution matrix" href="/docs/model-resolution-matrix" description="The (family, callClass) lookup the runtime uses to resolve a model at call time." />

  <Card title="Eval and replay" href="/docs/eval-and-replay" description="Recording, replay, and the diff engine the parity suite runs on top of." />

  <Card title="Cloud-routed agent" href="/docs/cloud-routed-agent" description="The sibling shape for buyers reaching the same models through Bedrock, Azure OpenAI, or Vertex." />

  <Card title="Regulated-domain agent" href="/docs/regulated-domain-agent" description="The HIPAA / FedRAMP / 21 CFR Part 11 shape, when the regulator drives the constraint instead of procurement." />
</Cards>
