pleach
Operate

Region-pinned agent

An agent that pins region, family, and model per call class — with parity-validation suites and deterministic failover discipline a procurement reviewer can read.

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 if the domain itself (HIPAA, FedRAMP, PCI, 21 CFR Part 11) drives the constraint. Multi-tenant SaaS agent if one runtime serves many region-pinned customers. 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

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

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.

// 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 for the cascade rules and Call classes for what each class covers.

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.

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

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 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

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

ColumnCarriesReviewer question it answers
record_idULID, monotonic"is the chain intact?"
tenant_idruntime context"which business unit?"
turn_idone per user message"what was the unit of work?"
call_classutility / reasoning / etc."which class served this?"
familyresolved family"did routing stay in the contract list?"
model_idresolved at call time"which model produced this?"
regionruntime context"did the call run in the cleared region?"
created_atserver clock"when, to the millisecond?"

See Auditable call row for the full column list.

Where to go next

On this page