MemClaw · Fleet Governance

Why Prompt-Level Data Separation Isn’t Enough
for Multi-Agent Systems

A system prompt that says “don’t surface legal data” runs afterretrieval has already loaded the GDPR hold into context. Three agents, one tenant, and a boundary that lives in the database query instead — where model capability has no bearing on whether it holds.

Caura.AI · 9 min read

TL;DR

  • Prompt-level separation fails because retrieval runs before any instruction executes. By the time the model reads “don’t surface legal data,” the hold is already scored, ranked, and in context.
  • MemClaw’s fleet model enforces the boundary at the database layer with a fleet predicate that runs before any retrieval scoring. The boundary is a SQL filter, not a model instruction— model capability has no bearing on whether it holds.
  • Three isolated partitions (fleet-sales, fleet-legal, fleet-org-shared) let Vera, Lex, and Axis share one tenant with no agent querying data outside its scope.
  • The admin agent (Axis) uses a mandatory fan-out — one recall per fleet — to produce labeled output that attributes each fact to its source partition before reasoning over conflicts.
  • In OSS, fleet boundaries rely on correct agent configuration; the managed service adds server-side token scoping that rejects out-of-scope recalls at the API layer.

The RBAC-framed companion to this piece is Prompt Rules Aren’t Access Control. This one goes deeper on the query predicate, identity fields, and the OSS-vs-managed enforcement boundary.

The problem with prompt-level data separation

The most common approach to multi-agent data governance is prompt-level instruction: tell each agent which domain it covers and what it must not surface. “You are a sales agent; do not discuss legal holds” appearsto set a boundary. It isn’t. When an agent calls a memory system, matching records load into the context window before the model processes any instruction. By the time it evaluates “don’t surface this,” the GDPR hold is already scored, ranked, and available for reasoning. A well-written prompt may suppress a direct answer. It does not prevent the model from reasoning over data it was never authorized to receive.

This gets worse as models improve.A more capable model finds more indirect paths to surface restricted information — not because it’s trying to violate a boundary, but because stronger reasoning and helpfulness both work against containment once restricted data is in context. Upgrading your model is a governance regression if your boundaries live in a prompt. The fix is a storage architecture that prevents restricted data from entering the retrieval result in the first place. This is what the memclaw-cross-fleet-gov repo implements: three agents (Vera/sales, Lex/legal, Axis/admin) on one tenant, with fleet boundaries enforced as a SQL predicate at the query layer.

Flat shared stores mix compliance and pipeline data

Placing all agents on a shared store feels practical — context accumulates, agents read each other’s writes, no coordination overhead on reads. The problem surfaces once agents write domain-specific data that carries real consequences if it crosses domain lines. In this demo, Vera has logged that Acme Corp’s deal is at $50k, late-stage, closing next week. Lex has written a GDPR hold on the same account: the DPA expired 2026-05-01, and no commercial progression can happen until a renewed DPA is signed. In a flat store, both records sit in the same retrieval pool. When Vera queries “Acme Corp deal status,” whether the hold surfaces depends on relevance ranking and whether her prompt makes her discard it. Neither is a governance boundary.

ApproachWhat it does rightWhere it breaks
Prompt-level separationEasy to configureData loads before policy runs; indirect questions bypass the instruction
Full siloing per agentTrue data separationAgents contradict each other; no mechanism to detect or surface conflicts
Flat shared memoryAgents share contextCompliance and pipeline data merge in one pool with no visibility control

Fleet partitioning enforces the boundary at the query, not the app layer

A fleet is a named logical partition on the memory store. Every record carries a fleet_id at write time. Every memclaw_recall declares a fleet_idsarray. Before any vector scoring, keyword ranking, or graph traversal runs, the database applies the fleet predicate. There is one caveat worth stating up front, because it’s the difference between a boundary that holds and one that leaks:

WHERE fleet_id IN (declared)
   OR fleet_id IS NULL
   OR visibility = 'scope_org'

Records written without a fleet_id (null) or with visibility: "scope_org" bypass fleet scoping and surface in anyagent’s recall. The demo’s isolation holds because every write uses an explicit fleet_id with the default scope_teamvisibility — not because the filter is unconditional.

Architecture diagram: an OpenClaw gateway dispatches to a Sales, Legal, and Admin agent, each loading the memclaw-governance skill; the MemClaw MCP server (fleet_ids enforced here) sits over three partitions — fleet-org-shared (all agents), fleet-sales (sales + admin), fleet-legal (legal + admin) — backed by a pgvector store with row-level tenant isolation, an 8-status lifecycle, and PII auto-quarantine on write.
Fig. 1 — Three fleets on one tenant. fleet_ids are enforced at the MemClaw MCP layer, before the hybrid search runs.

Vera’s workspace is configured for fleet-sales and fleet-org-shared. She has no knowledge that fleet-legalexists. When she queries Acme Corp, her recall only touches those two partitions — the hold Lex wrote to fleet-legal is not retrieved, not because Vera was instructed to ignore it, but because the SQL predicate never includes the partition it lives in.

AgentFleet accessExcluded from
Vera (sales)fleet-sales, fleet-org-sharedfleet-legal
Lex (legal)fleet-legal, fleet-org-sharedfleet-sales
Axis (admin)All threeNone — must label provenance on every result
memclaw_recall(
  agent_id: "sales-agent",
  fleet_ids: ["fleet-sales", "fleet-org-shared"],
  query: "Acme Corp deal status"
)

When Vera’s recall returns empty, the correct interpretation is “no record in this agent’s authorized partitions,” not “the account is clear across all domains.” Confirming the absence of a legal hold is a question for the legal agent, not an inference Vera can draw from her own recall returning nothing. You can test the boundary directly: write a GDPR hold from Lex, then run a recall from Vera on the same account. The result is empty.

Model capability cannot widen the data scope

Prompt-level separation has a counterintuitive failure mode: the more capable the model, the more likely it works around the boundary. Query-layer enforcement inverts that. The predicate runs before any retrieval scoring, before the model sees results, and before any instruction executes — the model is never handed records it wasn’t authorized to receive. The strength of that guarantee differs by configuration:

ConfigurationEnforcement layerMechanism
OSS (self-hosted)Agent-declaredThe agent assembles its own fleet_ids from its prompt/config — configurational scope, not structural enforcement
Managed serviceServer-sideAPI tokens carry a fleet-scope claim; any recall exceeding it is rejected at the API layer, regardless of what the agent declares

On the managed service, where API tokens carry a fleet-scope claim, the boundary is structural: the API rejects any recall exceeding the token’s scope regardless of what the agent passes. On OSS, the predicate enforces whatever fleet_idsthe agent declares — the isolation is as strong as the agent’s configuration, not a server constraint. For hard cross-domain isolation that can’t be overridden at any level, separate tenants per domain are the recommended architecture.

The admin agent fans out to preserve source attribution

Axis has access to all three fleets because its job is cross-fleet synthesis — detecting conflicts across domains and generating governance reports. But having access to all three doesn’t mean querying them together. Its AGENTS.md enforces a mandatory fan-out:

Step 1: memclaw_recall(fleet_ids: ["fleet-sales"])
Step 2: memclaw_recall(fleet_ids: ["fleet-legal"])
Step 3: memclaw_recall(fleet_ids: ["fleet-org-shared"])

Why three calls instead of one? A single recall with all three fleet IDs returns a unified ranked list — results merged and scored together. Fleet attribution is preserved as metadata, but the output is a blended pool. Axis needs to know which fleet each fact came from before it reasons, or its labeled output can’t correctly attribute the conflict parties. Three separate calls, labeled and merged by Axis, produce:

[fleet-sales]        Deal at late stage, $50k, close date next week.
[fleet-legal]        Active GDPR hold. DPA expired 2026-05-01.
[fleet-org-shared]   Enterprise account, 3-year relationship.

Conflict detected. Escalating to a human decision-maker.

Axis does not resolve the conflict — its job is to surface it with full attribution. After the fan-out it runs memclaw_insights with focus: "contradictions" to surface anything MemClaw flagged at write time. That same tool is how the synthesis gets persisted: memclaw_insights writes an insight-type memory server-side. Calling memclaw_write with memory_type: "insight"directly is rejected — insight (like outcome and rule) is server-reserved, and the write returns INVALID_ARGUMENTS directing you to memclaw_evolve (for outcome/rule) or memclaw_insights (for insight). The insight lands in fleet-org-shared, where both Vera and Lex read the conclusion without being granted access to each other’s underlying fleet data.

agent_id and fleet_id are two different controls

Every call takes two identity fields, and they look interchangeable but serve completely different functions. agent_id is required in the managed/tenant config (OSS falls back to an install-scoped default). fleet_idis optional and defaults to null — which is the dangerous case: a memory written without a fleet_id has no partition and becomes readable by every agent in the tenant. Both failures are silent: no error is raised; the agent simply operates on incomplete or mislabeled data.

FieldRoleWhat breaks if wrong
agent_idAttribution — records which agent wrote a memoryFalls back to an install-scoped default; authors on one instance become indistinguishable
fleet_id / fleet_idsScope — which partition the memory lives in and which agents can read itDefaults to null: the memory has no partition and becomes readable by every agent in the tenant

AI-written memory needs an explicit audit chain

When an agent writes a memory, the audit requirements are more demanding than for a human-written note — the agent may have synthesized across sources or updated a record another agent created. Every stored memory records its authoring agent_id, its fleet_id, its classified memory_type, and the entities extracted at write time. memclaw_manage traces a memory’s supersession chain— what it replaced and what replaced it — and each record carries its author for provenance, so a compliance review gets the exact sequence of reads and writes rather than a reconstruction from model logs.

MemClaw also runs contradiction detection on every write: if a new memory conflicts with an existing one about the same entity, both are flagged conflicted. A conflicted record is held out of the default recall — it resurfaces on an exact-term match or an explicit status: "conflicted" query, and memclaw_insights with focus: "contradictions"surfaces all flagged conflicts — so a correctly configured agent can treat it as contested and surface both sides rather than cite one as authoritative. (Memory carries an 8-status lifecycle: active, pending, confirmed, cancelled, outdated, conflicted, archived, deleted.)

Access control moves from inference time to write time

When access control lives in the system prompt, it executes at inference time on every call — consuming tokens, adding latency, and needing updates whenever the data or policy changes. Fleet-governed retrieval moves that cost to the storage layer: the WHEREpredicate runs in Postgres, not the model, and the write side does the work once — entity extraction, PII detection, contradiction checking, and classification all run when a memory is stored, leaving pre-enriched records for every subsequent recall.

Within the authorized scope, memclaw_recall fuses three methods: vector similarity (pgvector), full-text keyword search, and knowledge-graph traversal (two hops by default). memclaw_tuneadjusts the blend per agent — but as a single vector↔keyword scalar (fts_weight: 0 = semantic, 1 = keyword) plus graph reach (graph_max_hops), not three independent weights. Graph traversal is what fills the gaps pure vector search misses: if Vera queries “Acme Corp compliance status” and no direct match exists in her partitions, the traverser expands from the Acme entity to the GDPR entity to the Axis-authored insight in fleet-org-shared— surfacing the conclusion Axis wrote, never the underlying fleet-legal record, which stays outside her scope. As the store grows, the crystallizer batch-merges near-duplicates into canonical records, and memclaw_evolvecloses the loop — reporting an outcome that reinforces memories which led to correct behavior and deprecates those that didn’t.

Conclusion

If different agents handle different categories of sensitive data about the same accounts, prompt-level separation will eventually fail — rarely dramatically, usually as an agent being more helpful than it should be, or citing something it shouldn’t have accessed. The root cause is consistent: restricted data was loaded into context before any boundary was evaluated. The fleet model moves the boundary to where it actually enforces — the database query. Whether it holds depends on which fleet_ids are in the recall call, not on model capability or prompt quality. Clone the repo, write a legal hold from Lex, run a recall from Vera on the same account, and get an empty result.

FAQ

What exactly is a fleet, and how is it different from a tenant?

A fleet is a named logical partition within a tenant — a fleet_idcolumn on every record, with the fleet predicate applied before any search runs. There’s no separate database per fleet; a new fleet is created the first time you write with a new fleet_id. A tenant is physical isolation — a separate database and credentials. Use fleets for governance within one org; use separate tenants when isolation must be physical (e.g. between customers).

What happens if an agent passes a fleet_id it isn't authorized to access?

In OSS, it retrieves data from that fleet — there is no server-side enforcement stopping it; the boundary relies on agents being correctly configured. The managed service issues tokens scoped to specific fleets and rejects any recall including a fleet outside the token’s scope, regardless of what the agent passes.

Why does the admin agent make three separate recall calls instead of one?

A single recall with multiple fleet IDs returns a unified ranked list — results merged and scored together, attribution preserved only as metadata. Axis needs results separated by source to write a labeled synthesis (“[fleet-sales] says X, [fleet-legal] says Y, conflict”). Three calls, labeled before merging, make that attribution unambiguous.

How does MemClaw detect contradictions automatically?

On every write, MemClaw extracts the entities in the new memory and checks existing memories about those entities for conflicting state. If a new write says “Acme DPA valid through 2028” and an existing one says “expired 2026-05-01,” both are flagged conflicted. Call memclaw_insights with focus: "contradictions" to surface all flagged conflicts across your authorized fleets at any time.

Verify the boundary yourself.

Write a legal hold from Lex, recall it from Vera, get an empty result.

MemClaw is governed shared memory for AI agent fleets — multi-agent, multi-fleet, multi-tenant, with permissions and audit trails. Built by Caura.ai.