Testing An AI Data Assistant
June 29, 2026 ยท View on GitHub
Use this workflow when an application has an Ask AI, BI copilot, warehouse chat, or text-to-SQL tool that can call SQL.
1. Create A Scanner Scaffold
uv run policystrata init-scan --out policystrata
This creates:
policystrata/policystrata.yaml
policystrata/domain/policy.yaml
policystrata/domain/surfaces.yaml
policystrata/traces.example.jsonl
Run the generated command once before wiring production traces:
uv run policystrata scan --config policystrata/policystrata.yaml --out runs/policystrata-smoke
2. Map Tool Arguments To Semantic IR
Capture the user-facing query plan before SQL lowering:
{
"metric": "ticket_count",
"dimensions": ["region"],
"filters": {"severity": "high"},
"time_range": "last_month",
"grain": "month",
"limit": 100
}
Keep this mapping independent from the SQL compiler. PolicyStrata uses it to ask the policy oracle whether the request should have been authorized before comparing SQL behavior.
3. Capture Tool-Call SQL
Emit one JSONL line per SQL tool call:
{
"id": "ask_ai_2026_06_24_001",
"principal": "acme_analyst",
"tenant_ids": ["acme"],
"semantic_ir": {"metric": "ticket_count", "dimensions": ["region"], "limit": 100},
"sql": "select count(distinct support_tickets.id) as value from accounts left join support_tickets on support_tickets.account_id = accounts.id where accounts.tenant_id in ('acme') group by accounts.region limit 100",
"release_allowed": true,
"source": "ask_ai"
}
If SQL relies on RLS rather than literal tenant predicates, do not use trace-supplied metadata as a
scanner control. Add RLS or state assertions under database: so the containment layer is checked
from trusted scan configuration.
4. Configure Tenancy Vocabulary
Replace built-in tenant names with your application terms:
tenancy:
canonical_predicates:
- "transactions.household_id = :principal.tenant_id"
- "accounts.household_id = :principal.tenant_id"
tenant_columns:
- transactions.household_id
- accounts.household_id
Use this for household_id, organization_id, RLS GUCs, join-derived ownership, or helper
functions that are canonical in your application.
5. Run The Scanner
uv run policystrata scan --config policystrata/policystrata.yaml --out runs/policystrata
Review:
runs/policystrata/report.md
runs/policystrata/findings.jsonl
runs/policystrata/witnesses/*.json
Findings include:
what_changedownerprobable_fixminimal_repro_traceci_gate_command
6. Fail CI On Unsafe Drift
uv run policystrata scan --config policystrata/policystrata.yaml --out runs/policystrata
uv run policystrata doctor --config policystrata/policystrata.yaml --strict
scan exits 1 on high-confidence gate failures such as unsafe release, missing tenant scope, RLS
leakage, or semantic drift. doctor --strict exits 1 when stack wiring is missing, partial, or
invalid. Keep trace generation deterministic in CI by replaying sanitized tool-call fixtures rather
than requiring an LLM API key.