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:

  1. the semantic layer that models tables, relationships, calculated fields, cubes, metrics and business rules;
  2. 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/v1 WrenAI 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/v1 branch.

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_sources and sync_tables metadata 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_id is never taken from the question, generated SQL or LLM tool arguments;
  • connection_ref is 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.orders can pass when the allowed semantic model is named orders;
  • 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_r42 receives USAGE only on schema sync_r42;
  • it receives SELECT only on approved tables in sync_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-ai metapackage 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:

  1. Interpret the user’s requested measure, dimensions and time period.
  2. Retrieve only relevant semantic context.
  3. Recall confirmed examples.
  4. Generate SQL against Wren model names.
  5. Run wren_dry_plan.
  6. Execute with wren_query.
  7. Retry at most twice for correctable semantic/SQL errors.
  8. Produce an answer containing period, currency/timezone assumptions, truncation and confidence.
  9. 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_tables and 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:

  1. apply the existing POS pack;
  2. run compatibility and control queries;
  3. request confirmation only for differences;
  4. 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

  1. Browser posts a question with its existing JWT.
  2. Go validates the session and restaurant ownership.
  3. Go checks the venue’s assistant is on and its daily token limit, counting what running questions will spend.
  4. Go creates ai_query_runs(status=pending).
  5. Go signs a short-lived internal context.
  6. Python validates the context and resolves the immutable semantic version.
  7. Python resolves the venue’s Wren engine and connection pool.
  8. PydanticAI retrieves relevant context/examples.
  9. The LLM generates semantic SQL.
  10. Wren dry-plans the SQL in strict mode.
  11. SyncaPos performs the additional planned-SQL policy check.
  12. Wren executes through the tenant-specific PostgreSQL login.
  13. PostgreSQL RLS/grants enforce the physical tenant boundary.
  14. The bounded result is returned to the agent.
  15. The LLM composes a streamed answer.
  16. Go persists the final status, usage and audit.
  17. 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/v1 is 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.