AI Analytics with Wren
Status: implemented
Scope: backend architecture for natural-language analytics over a venue’s own data
1. Executive decision
SyncaPos will use Wren as both:
- the semantic layer that models tables, relationships, calculated fields, cubes, metrics and business rules;
- the SQL planner that turns semantic SQL into the query the database runs.
The planned query runs on the venue’s own pool of PostgreSQL connections, under the venue’s login.
SyncaPos will not reuse the old WrenAI application from legacy/v1. The public API, user authentication, restaurant ownership, conversations and frontend remain SyncaPos components.
The final responsibility split is:
| Responsibility | Owner |
|---|---|
| Browser authentication and restaurant ownership | Go SyncaPos API |
| Conversation API, spend ceilings and audit | Go SyncaPos API |
| Agent loop and external LLM integration | PydanticAI service |
| Semantic models, relationships, cubes and SQL planning | Wren |
| SQL execution and result conversion | The venue’s connection pool in the AI service |
| Physical restaurant isolation | PostgreSQL roles, grants and RLS |
| Semantic onboarding of an unknown POS | SyncaPos discovery workflow plus Wren MDL |
| Frontend | Custom SyncaPos frontend |
Wren is trusted to plan and execute queries, but it is not the tenant-security boundary. PostgreSQL permissions must make a cross-restaurant query impossible even if Wren, the LLM or SyncaPos validation contains a bug.
2. Product goals
The backend must answer questions such as:
- What were last week’s sales for waiter Stevenson?
- Which products generated the most revenue this month?
- What was the average check by weekday?
- How much tax, discount and tip was recorded yesterday?
- Which orders were cancelled or refunded?
- Which products are commonly purchased together?
Across every deployment:
- the JWT and restaurant ownership model is authoritative;
- arbitrary synchronized POS databases are stored in the separate sync database, one schema per venue:
sync_r<restaurant_id>; - no venue may reach another venue’s rows or mirrored schema. That is the boundary the assistant is confined by, and it is enforced by PostgreSQL rather than by the application.
2.1 Deployment
- The complete Go API, AI service, Wren runtime and database storage run inside customer infrastructure.
- The initial deployment serves one configured restaurant/database.
- The LLM may remain external and is configured using the customer’s API key.
- Database credentials, semantic mappings, durable conversation storage and query audit remain local. For a follow-up, a bounded window of completed conversation messages is sent to the administrator-configured external LLM.
- The initial target is the synchronized PostgreSQL mirror. Direct querying of a live POS database is a later, separately reviewed option.
3. Non-goals
The first release will not:
- copy or run the AGPL
legacy/v1WrenAI backend or frontend; - expose Wren MCP or Wren HTTP services directly to end users;
- allow the browser or LLM to supply a connection string, database, schema, role or physical table namespace;
- promise certified business answers from an unknown POS schema without confirmation;
- query every possible live POS database directly;
- implement the frontend as part of this backend initiative;
- allow arbitrary data-modifying SQL.
4. Verified upstream and license baseline
The license review used complete clones of:
Canner/WrenAI, including all branches and tags;- archived
Canner/wren-engine; - the
legacy/v1branch.
Audit references:
- WrenAI
main:73c1255439795ac9cf538b706ec4c208d6d3f25b; - WrenAI
legacy/v1:e42b8d057c611016d781c7bbe74ba4e5aeb9712d; - archived
wren-engine:bc2b06a1950a946aa9bd16356fe0c84e9dc504bc.
4.1 Selected packages
| Component | Candidate version | License | Use |
|---|---|---|---|
wrenai | 0.13.2 | Apache-2.0 | Wren engine, MDL and Postgres connector |
wren-core-py | 0.7.3 | Apache-2.0 | Rust semantic engine Python binding |
wren-pydantic | 0.2.1 | Apache-2.0 | Reference implementation; not used unchanged |
| Pydantic | 2.13.4 | MIT | Structured validation |
| PydanticAI Slim | 1.107.1 | MIT | Agent and tool orchestration |
| SQLGlot | 29+ | MIT | Additional SQL inspection |
| Psycopg / psycopg-binary | 3.3.4 | LGPL-3.0-only | PostgreSQL driver of the venue connection pool |
4.2 Wren repository license map
The current repository assigns:
core/**: Apache-2.0;sdk/**: Apache-2.0;skills/**: Apache-2.0;examples/**: Apache-2.0;docs/**: CC BY 4.0;- root source files: Apache-2.0.
The root contains the full AGPL-3.0 text for possible future modules. No current path is assigned to it. The old full WrenAI application preserved in legacy/v1 is AGPL-3.0 and is excluded.
4.3 Runtime dependency policy
Queries run on Psycopg 3, as Wren’s own PostgreSQL connector does. Psycopg is LGPL-3.0-only. This is not AGPL and does not require opening the SyncaPos application when used as an unmodified library, but it is a reviewed weak-copyleft dependency and must be included in notices/SBOM. It must not be locally modified without a separate compliance review.
The production policy is:
- deny AGPL, GPL application dependencies, SSPL, Elastic License and unknown licenses;
- allow MIT, BSD, Apache-2.0, PostgreSQL License and ISC;
- allow MPL/LGPL only through an explicit reviewed exception;
- never install
wrenai[all]; - install only the required Wren extras;
- pin every direct and transitive version in a lockfile;
- produce an SPDX or CycloneDX SBOM for every release;
- repeat the Wren path-to-license audit on every Wren upgrade.
If LGPL is later disallowed by company policy, the fallback is a PostgreSQL driver under an Apache/BSD license, such as asyncpg or pg8000, behind the same venue pool.
5. Existing SyncaPos boundaries
The current backend already provides most of the required tenant context:
- console JWT validation in Go;
- database-backed session invalidation;
- restaurant ownership checks returning 404 for foreign resources;
- the fixed main database;
- a separate sync database;
- physical sync schemas named
sync_r<restaurant_id>; sync_sourcesandsync_tablesmetadata describing arbitrary mirrored tables;- existing restaurant timezone and currency;
- existing order, invoice, tax, cancellation, menu and employee projections.
AI Analytics must reuse these boundaries rather than introduce a parallel identity model.
6. Component architecture
┌────────────────────────────────────────────────────────────────────┐
│ Custom frontend │
│ - conversations │
│ - answer stream │
│ - SQL/assumptions │
│ - semantic confirmation UI │
└──────────────────────────────┬─────────────────────────────────────┘
│ existing JWT
▼
┌────────────────────────────────────────────────────────────────────┐
│ Go SyncaPos API │
│ - validates session │
│ - checks restaurant ownership │
│ - creates conversation/query-run records │
│ - applies the venue's daily token limit │
│ - signs short-lived internal tenant context │
│ - streams/cancels the AI request │
└──────────────────────────────┬─────────────────────────────────────┘
│ private authenticated request
▼
┌────────────────────────────────────────────────────────────────────┐
│ Python AI service │
│ - PydanticAI Slim │
│ - tenant runtime resolver │
│ - semantic artifact loader │
│ - Wren context/memory │
│ - bounded agent loop │
│ - Wren strict planning and execution │
│ - result/answer limits │
└──────────────────────┬──────────────────────┬──────────────────────┘
│ │
│ selected context, │ tenant-specific
│ question, bounded │ Wren connection
│ query result │
▼ ▼
┌─────────────────┐ ┌───────────────────────────────┐
│ External LLM │ │ PostgreSQL │
│ customer/server │ │ - fixed main DB │
│ API key │ │ - arbitrary sync DB │
└─────────────────┘ │ - tenant roles / grants / RLS │
└───────────────────────────────┘
The AI service is private. It does not accept browser JWTs and is not exposed through nginx.
7. Authentication and tenant context
7.1 Public request
The browser calls a route under:
/api/restaurants/:restaurant_id/ai/*
The existing Go JWT middleware authenticates the user. Before starting an AI request, Go calls the existing owner-scoped restaurant repository. A foreign or deleted restaurant returns 404.
7.2 Internal context
After ownership succeeds, Go creates a short-lived signed context containing:
{
"request_id": "uuid",
"user_id": 123,
"restaurant_id": 42,
"conversation_id": "uuid",
"source_profile": "syncapos|pos_sync",
"semantic_profile_id": "uuid",
"semantic_version": 7,
"connection_ref": "opaque-reference",
"expires_at": 1785340000
}
Rules:
- the AI service rejects expired, unsigned or replayed contexts;
restaurant_idis never taken from the question, generated SQL or LLM tool arguments;connection_refis opaque and resolvable only by the backend;- the context lifetime should be no longer than the maximum answer duration;
- request and conversation IDs are included in every log and query audit row.
Replay consumption is stored in the migrated main PostgreSQL database as a SHA-256 nonce digest with expiry. The insert is atomic across service processes and replicas; a replay-store outage fails authentication closed with 503.
8. PostgreSQL tenant isolation
8.1 Why database-enforced isolation is mandatory
A local test against published Wren 0.13.2 showed:
- strict mode rejects an unknown table name;
- a schema-qualified
sync_r43.orderscan pass when the allowed semantic model is namedorders; - the foreign schema qualification can survive in planned SQL.
Consequently, Wren strict mode and SQLGlot are defense in depth. Database permissions are the final boundary.
8.2 One role model
This is the model §8.4 describes: each venue’s assistant connects as its own syncapos_ai_r<id> login, which venue that is comes from an immutable mapping the session cannot change, and row-level security decides what that role may see. There is no function for switching between venues, because nothing needs to switch.
8.3 The application database
Every physical table reachable from a fixed-pack security-invoker view has ENABLE ROW LEVEL SECURITY and FORCE ROW LEVEL SECURITY. Policies name syncapos_ai_reader and resolve the venue from ai.current_restaurant_id(), which reads the immutable role mapping - not from a session value the connection could set.
The semantic pack references the curated ai schema. Security-invoker views require selected base-column privileges, so direct access to those same columns is possible but remains RLS-scoped. Raw receipt payloads, extraction JSON/errors, credentials, users, sessions and unrelated tables remain denied.
A session whose role maps to no venue resolves to NULL and sees no rows. Child relations without a direct restaurant_id are filtered through their authoritative parent order, restaurant or POS-system relationship.
8.4 Arbitrary sync database
For restaurant 42:
syncapos_ai_r42receivesUSAGEonly on schemasync_r42;- it receives
SELECTonly on approved tables insync_r42; - it has no privilege on
sync_r43, registry tables or system schemas beyond normal catalog metadata; - default privileges for the schema’s catalog owner ensure newly mirrored tables receive the intended grants;
- the sync ingestion owner remains separate from the AI reader.
AI provisioning is an explicit control-plane operation. The sync ingestion path does not mutate AI grants; provisioning grants current relations, installs owner-derived default privileges, and fails loudly on incomplete coverage.
8.5 The installation’s own roles
The deployment uses the syncapos_ai_r<restaurant_id> convention and the readonly provisioning code. An installation commonly has a handful of venues and one mirror database; the role is unable to create objects or reach unapproved schemas and relations. Strict Wren configuration and query limits remain enabled even where one owner holds every venue and isolation between them is not a boundary between companies.
8.6 Credential lifecycle
- Generate cryptographically random passwords.
- Store only encrypted credentials or references to a secret manager.
- Never place tenant DB passwords in semantic artifacts, prompts, messages or logs.
- Cache credentials only in process memory for a bounded time.
- Rotate credentials without changing semantic versions.
- Close the venue’s connection pool when a password is rotated, a restaurant is deleted or access is revoked.
The executable lifecycle and verification procedure is documented in AI database roles (the ai-database-roles runbook, kept with the deployment it describes).
9. Wren runtime integration
9.1 Use Wren Core directly
The runtime should construct WrenEngine from:
- an immutable compiled MDL manifest;
- the server-selected data source;
- the server-resolved tenant credential;
WrenConfig(strict_mode=True, ...);- required Wren session properties.
It should not use WrenToolkit.from_project() unchanged because that adapter:
- resolves filesystem/global Wren profiles;
- caches a connector inside one toolkit;
- assumes one toolkit/project binding;
- does not expose all required tenant/security configuration;
- depends on the full
pydantic-aimetapackage rather than a minimal provider set.
9.2 Thin SyncaPos adapter
Implement a small TenantWrenRuntime around Wren Core: Wren plans, and the planned SQL runs on the venue’s connection pool.
Responsibilities:
- validate the signed context;
- load the correct semantic artifact;
- resolve the tenant connection;
- always enable Wren strict mode;
- pass fixed-database RLS properties where applicable;
- create/reuse only tenant-scoped Wren engines and connection pools;
- call Wren
dry_plan, then run exactly the SQL it planned on the venue’s pool; - normalize Wren errors for PydanticAI;
- enforce service-level limits and audit.
Suggested cache key:
(deployment_id, restaurant_id, source_profile, semantic_profile_version, credential_version)
The cache must never reuse an engine or a pool across different restaurant IDs.
A venue’s questions run side by side: each query takes a connection from the venue’s pool and gives it back, and Stop cancels that query only. Nothing rations how many questions a venue asks; the daily token limit is the one guard on what it spends.
9.3 Wren configuration
Production defaults:
strict_mode = true
fallback = false
default row limit = 200
hard row limit = 1000
statement timeout = 15 seconds
connection timeout = 5 seconds
max answer wall time = 45 seconds
max tool calls = 8
max SQL retries = 2
max result payload = 1 MiB
daily token limit = 10,000,000 per venue unless the venue sets its own
Discovery jobs use a separate queue and may have larger timeouts, but only for server-generated profiler SQL.
The denied-function list must include role/session mutation, file/network readers, large-object access and cross-database extensions. At minimum:
set_config
pg_read_file
pg_read_binary_file
pg_ls_dir
lo_import
lo_get
dblink
dblink_exec
postgres_scan
postgres_query
read_csv
read_parquet
read_json
SQLGlot performs an additional inspection of planned SQL:
- exactly one query;
- SELECT/CTE/set operation only;
- no DDL/DML/COPY/CALL/DO/transaction commands;
- reject unapproved explicit schemas;
- reject system/registry tables;
- reject denied functions.
This inspection improves diagnostics and blocks obvious attempts before a database round trip. PostgreSQL privileges remain authoritative.
9.4 Result limits
Wren applies a row limit before retrieving PostgreSQL results. SyncaPos additionally:
- excludes binary/receipt payload columns from semantic models;
- excludes unbounded sensitive text by default;
- checks Arrow/result byte size before sending it to the LLM;
- truncates or rejects oversized results;
- runs the AI service with container/process memory limits;
- instructs the agent to aggregate in SQL rather than retrieve raw rows.
If production testing shows that a single large cell can exceed safe memory before the post-query check, extend the venue pool with incremental fetch and byte accounting.
10. PydanticAI integration
Use pydantic-ai-slim with only the selected OpenAI-compatible provider dependencies.
The agent receives these tools:
| Tool | Purpose |
|---|---|
list_models | List available semantic models/cubes |
describe_model | Get columns, descriptions and relationships |
retrieve_context | Retrieve relevant business rules/schema context |
recall_confirmed_queries | Retrieve confirmed NL-to-semantic-SQL examples |
wren_dry_plan | Validate and expand semantic SQL without executing |
wren_query | Plan through Wren and run on the venue’s connection pool |
There is no raw/unrestricted database tool.
Agent workflow:
- Interpret the user’s requested measure, dimensions and time period.
- Retrieve only relevant semantic context.
- Recall confirmed examples.
- Generate SQL against Wren model names.
- Run
wren_dry_plan. - Execute with
wren_query. - Retry at most twice for correctable semantic/SQL errors.
- Produce an answer containing period, currency/timezone assumptions, truncation and confidence.
- Store a query as confirmed only after explicit confirmation or a successful golden/control-total check.
The implemented tools, limits and response schema are specified in the governed executor runbook (the ai-governed-executor runbook, kept with the deployment it describes). The implemented browser-to-Go streaming, reconnect, cancellation and feedback contract is documented in the AI console API runbook (the ai-console-api runbook, kept with the deployment it describes).
11. External LLM boundary
The following may be sent to the configured LLM:
- the user’s question;
- selected semantic model descriptions;
- relevant business rules;
- confirmed semantic SQL examples;
- generated semantic/planned SQL;
- bounded query results required to compose the answer;
- bounded discovery evidence when value sampling is enabled.
The following must never be sent:
- DB credentials;
- device tokens;
- JWTs or internal service tokens;
- OpenRouter/LLM API keys;
- raw receipt bytes;
- unrestricted tables;
- session or password data;
- unrelated restaurant data.
On-prem configuration supports:
- external LLM with customer key;
- custom OpenAI-compatible base URL;
- outbound proxy;
- custom CA;
- schema-only discovery;
- sampled discovery with redaction;
- optional future local LLM endpoint.
The UI and deployment documentation must clearly state that questions, selected schema context and bounded query results leave the installation when an external LLM is used.
12. Semantic models
12.1 Fixed receipt/order pack
One immutable pack version is shared by every venue whose data it fits.
Models:
- restaurants/timezone/currency context;
- receipts and extraction status;
- logical orders;
- order items/options;
- invoices;
- taxes;
- discounts;
- tips;
- cancellations/refunds;
- waiters/employees;
- menu products.
Initial metrics/cubes:
- gross and net sales;
- order count;
- average check;
- sales by waiter;
- sales by product/category;
- sales by hour/day/week/month;
- taxes, discounts and tips;
- cancellations/refunds;
- top products and product mix.
The pack must follow existing product semantics:
- logical orders are not raw receipt snapshots;
- cancellations and deletes match current Orders/Invoices behavior;
- restaurant-local timezone defines relative dates;
- currency is explicit;
- test receipts never count as sales.
The implemented object grains, formulas, caveats and versioning procedure are defined in the SyncaPos fixed semantic pack runbook (the syncapos-semantic-pack runbook, kept with the deployment it describes).
12.2 Arbitrary POS stage-zero model
The existing sync metadata can generate a syntactically correct raw MDL:
- one Wren model per approved mirrored table;
- columns/types from
sync_tablesand the physical catalog; - keys from
key_columns; - physical table reference fixed to the current restaurant schema.
This makes the database queryable but does not make business answers trustworthy. Relationships, status meanings and metrics require discovery/confirmation.
12.3 Pack hierarchy
built-in fixed pack
or
POS-family/version base pack
+
restaurant-specific overlay
=
immutable compiled semantic artifact
Confirmed semantic SQL examples are stored against a semantic version, not globally without context.
13. Unknown POS discovery
DDL alone cannot determine which table is an order, which status means final or how refunds affect sales. Discovery is therefore evidence-based and confirmable.
13.1 Schema fingerprint
Create canonical schema JSON from:
- source/scheme/version;
- tables and columns;
- PostgreSQL types;
- key columns and replace keys;
- nullability;
- safe catalog metadata;
- bounded row-count metadata.
Calculate:
- an exact
schema_hash; - a softer compatibility fingerprint for reusing a POS-family pack.
The implemented behavior is documented in POS schema snapshots (the pos-schema-snapshots runbook, kept with the deployment it describes).
13.2 Deterministic profiler
Profiler queries are generated by backend code, not the LLM.
It collects bounded evidence:
- row counts;
- null ratios;
- numeric/date ranges;
- bounded distinct statuses/types/payment methods;
- uniqueness of candidate keys;
- sampled join coverage;
- name/type similarity;
- candidate order, line, payment, product and employee shapes.
Controls:
- per-table timeout;
- maximum sampled rows/distinct values;
- PII detection/redaction;
- no binary fields;
- schema-only mode;
- resumable jobs;
- no failure of the whole run when one table cannot be profiled.
The implemented behavior is documented in Unknown POS schema profiler (the pos-schema-profiler runbook, kept with the deployment it describes).
13.3 Proposal and confirmation
The LLM may propose:
- order/header and line tables;
- employee, product, payment and tax tables;
- joins/cardinalities;
- business timestamps;
- amount/quantity/discount/tip/tax fields;
- final/cancelled/refund/test status values;
- control-total queries.
State machine:
draft -> proposed -> confirmed/rejected -> published -> stale
Only a published version is certified. Unconfirmed exploratory answers must show confidence and assumptions.
The implemented review, control, publication and rollback behavior is documented in POS semantic confirmation and publication (the pos-semantic-confirmation runbook, kept with the deployment it describes).
13.4 Reuse
When another restaurant has a compatible fingerprint:
- apply the existing POS pack;
- run compatibility and control queries;
- request confirmation only for differences;
- publish a restaurant overlay when required.
The implemented base-template, overlay, version binding and runtime material behavior is documented in Reusable POS semantic packs (the pos-reusable-packs runbook, kept with the deployment it describes).
14. Storage model
Proposed main-database entities:
| Entity | Purpose |
|---|---|
ai_conversations | User conversation per restaurant |
ai_messages | User/assistant messages |
ai_query_runs | SQL, semantic version, timing, usage, status |
ai_feedback | Positive/negative feedback and reason |
semantic_profiles | Fixed/POS/restaurant profile identity |
semantic_profile_versions | Immutable published versions |
semantic_examples | Confirmed NL-to-semantic-SQL pairs |
semantic_proposals | Discovery evidence and confirmation state |
schema_snapshots | Canonical schema and fingerprints |
ai_tenant_credentials | Encrypted credential reference/version |
Every tenant-owned record contains restaurant_id. Repository methods must enforce ownership using the same pattern as existing restaurant resources.
Store:
- question and final answer;
- semantic SQL and Wren-planned SQL;
- model/provider name;
- semantic profile/version;
- duration/token/cost counters;
- row count/truncation;
- error phase/class;
- bounded result preview only when enabled.
Never store plaintext DB or LLM credentials.
The implemented schema, retention behavior and delivered conversation CRUD are documented in AI storage (the ai-storage runbook, kept with the deployment it describes).
15. Public backend API
Suggested routes:
POST /api/restaurants/:id/ai/conversations
GET /api/restaurants/:id/ai/conversations
GET /api/restaurants/:id/ai/conversations/:conversationId
DELETE /api/restaurants/:id/ai/conversations/:conversationId
POST /api/restaurants/:id/ai/conversations/:conversationId/messages
POST /api/restaurants/:id/ai/runs/:runId/cancel
POST /api/restaurants/:id/ai/runs/:runId/feedback
GET /api/restaurants/:id/ai/semantic
GET /api/restaurants/:id/ai/semantic/proposals
PATCH /api/restaurants/:id/ai/semantic/proposals/:proposalId
POST /api/restaurants/:id/ai/semantic/publish
Message posting streams events:
accepted
retrieving_context
planning
executing
answer_delta
warning
completed
error
The stream may expose user-safe SQL and assumptions, but never internal prompts, credentials or unrestricted database errors.
16. Internal AI service API
Suggested private endpoints:
POST /v1/answers:stream
POST /v1/semantic/validate
POST /v1/semantic/compile
POST /v1/discovery/propose
GET /healthz
GET /readyz
The answer request carries the signed context and question. It does not carry an arbitrary DSN.
Errors use stable phases:
AUTH_CONTEXT
SEMANTIC_LOAD
CONTEXT_RETRIEVAL
LLM
SQL_PLANNING
SQL_POLICY
SQL_EXECUTION
RESULT_LIMIT
CANCELLED
INFRASTRUCTURE
17. Request lifecycle
- Browser posts a question with its existing JWT.
- Go validates the session and restaurant ownership.
- Go checks the venue’s assistant is on and its daily token limit, counting what running questions will spend.
- Go creates
ai_query_runs(status=pending). - Go signs a short-lived internal context.
- Python validates the context and resolves the immutable semantic version.
- Python resolves the venue’s Wren engine and connection pool.
- PydanticAI retrieves relevant context/examples.
- The LLM generates semantic SQL.
- Wren dry-plans the SQL in strict mode.
- SyncaPos performs the additional planned-SQL policy check.
- Wren executes through the tenant-specific PostgreSQL login.
- PostgreSQL RLS/grants enforce the physical tenant boundary.
- The bounded result is returned to the agent.
- The LLM composes a streamed answer.
- Go persists the final status, usage and audit.
- Cancellation at any point closes the LLM stream and cancels/closes the active database query.
18. Failure behavior
- Invalid browser session: 401.
- Foreign/deleted restaurant: 404.
- AI feature unavailable/disabled: 503 or product-specific 403.
- Daily token limit reached: 429 with retry information.
- LLM timeout: retry only when safe, then return a user-facing retryable error.
- Wren planning error: bounded model retry with error context.
- PostgreSQL permission denial: never retry as a SQL correction; record a security/policy failure.
- Statement timeout: explain that the question is too expensive and suggest aggregation/narrower dates.
- Truncated result: answer must disclose truncation.
- AI service failure must not affect receipt ingestion, sync ingestion or the normal console.
There must be a kill switch that disables all AI query execution without disabling data synchronization.
19. Evaluation and release gates
19.1 Fixed pack
Build at least 50 golden questions covering:
- restaurant-local periods;
- waiter and product filters;
- totals/averages/counts;
- orders/items joins;
- taxes/discounts/tips;
- cancellations/refunds;
- empty or ambiguous results;
- malicious prompt/SQL attempts.
Compare results with existing Orders, Invoices, Taxes, Menu and Employees behavior.
19.2 Unknown POS
For each certified pack:
- maintain control totals;
- verify join coverage;
- verify status filters;
- test schema-compatible upgrades;
- test stale detection after breaking changes.
19.3 Mandatory gates
- 100% tenant-isolation/adversarial tests;
- no AGPL/GPL dependency in release image;
- reviewed LGPL/MPL notices present;
- numeric-answer accuracy threshold agreed before pilot;
- no rollout when evaluation regresses;
- canary/feature flag by restaurant;
- tested rollback and kill switch.
20. Observability and audit
The executable operator controls, metrics API, alert thresholds, canary gate and incident response are defined in AI Analytics production operations (the ai-production-operations runbook, kept with the deployment it describes).
Metrics:
- answer latency;
- LLM latency/tokens/cost;
- tool-call and SQL retry counts;
- Wren planning failures;
- query duration/timeouts;
- result rows/bytes/truncation;
- policy/permission rejections;
- active queries per restaurant;
- semantic profile/version usage;
- discovery/proposal conversion.
Logs:
- correlate browser, Go, Python, LLM and database with
request_id; - include restaurant ID and semantic version;
- redact questions/results according to configured privacy policy;
- never log credentials, internal tokens or unrestricted query results.
21. Deployment
21.1 Process topology
- Go remains the public process.
- The Python AI service runs as a separate private service/container.
- It has outbound access only to configured LLM endpoints and required databases.
- Connection pools are bounded globally and per venue.
- AI deploys independently, so it can be disabled or rolled back without touching ingestion.
21.2 Packaging
The executable Compose topology, install, egress, backup/restore, upgrade/rollback and key-rotation procedures are in the on-prem deployment runbook (the ai-onprem-deployment runbook, kept with the deployment it describes).
Initial package:
- Go API;
- Python AI/Wren service;
- PostgreSQL main/sync/semantic storage;
- reverse proxy;
- customer-managed LLM configuration.
Docker Compose is the first supported packaging format. Installation documentation must cover:
- TLS and outbound proxy;
- DB/LLM key rotation;
- backup/restore;
- semantic version preservation;
- upgrade/rollback;
- SBOM/notices;
- optional telemetry, disabled by default unless agreed.
22. Definition of done
- Wren performs semantic planning; the planned SQL runs on the venue’s own connection pool.
- No code or package from Wren
legacy/v1is used. - Every query uses a PostgreSQL login physically restricted to one venue.
- Cross-restaurant access fails at the database layer.
- The LLM cannot select credentials, DB, schema or role.
- Fixed-schema answers match existing application control totals.
- Unknown POS semantics are evidence-based, versioned and confirmable.
- An installation works without a runtime dependency on any service of ours.
- External LLM data flow is explicit and configurable.
- Every query is auditable by user, restaurant, semantic version and request.
- Release images have pinned dependencies, SBOM and required license notices.