3.1 · The anatomy of a guard
A guard is a governed query surface that requires a trusted scope and refuses to run without it. The scope arrives as a runtime client attribute — set by a trusted backend (an embedded app’s server, an API integration), never typed by the end user:Prompt
You’ll see: a query surface whose WHERE clause is built from the required scope, with an explicit error path when the scope is absent. That error path is the point: fail closed means the failure mode of a broken integration is “no data,” never “all data.”
3.2 · The rules that keep guards trustworthy
- Scope comes from a trusted backend — a server your team controls sets the attribute. Anything a browser or end user can set is a preference, not a boundary.
- Never expose the entitlement field as a caller-controlled override — no “optional region parameter” that quietly widens scope.
- Centralize shared guards — one reviewed module that other surfaces import, not a copy-pasted WHERE clause per file (copies drift; one of them will be wrong).
- Interactive members are scoped differently — for humans in chat, the boundary is their warehouse identity (Module 1) or a role-scoped connector; guards + client attributes are the pattern for embedded and API traffic. Don’t build a client-attribute guard and assume it scopes your analysts.
3.3 · Wire it to the personas
Point each persona’s behavioral context (the Context Stack personas) at exactly these governed surfaces — the persona’s “allowed data surfaces” list and the enforced surface set should be the same list. Persona says should, guard says can, and they agree.✅ Checkpoint
- At least one fail-closed guard exists as governed .tql, and you inspected its rendered SQL
- Removing the scope attribute produces an error, not unscoped rows — tested
- Shared guard logic lives in one reviewed module, not copies
- You can say which traffic guards protect (embedded/API) vs what protects interactive members