The problem: valid SQL that answers a different question

A query that runs without error can still count something the user never asked about. Which table and column 'order count' means, which period and scope apply, whether an aggregate word like 'total' is a unit — none of this is for a model to guess. So this engine looks for accuracy in declarations rather than prompts.

From question to result: four layers and where the model sitsNatural-language questionasked in business terms — table, column, period and scope are not yet decidedSemantic layerA domain pack binds terms and synonyms to declared metrics, dimensions and columnsInterpreted only inside the selected data scope · ambiguous values stop at a gateValidation layerSyntax tree inspected with sqlglot · a single SELECT onlyForbidden nodes, multiple statements and unresolved parameters rejected before executionExecution layerRead-only account · session statement_timeout · EXPLAIN cost and row capsOwnership, prior validation and live permissions rechecked at approvalEvidence layerAnswer figures checked against returned values and units (numeric grounding)Question, query, approval, execution context and per-stage timings kept in the audit recordModel: proposes an SQL candidate — a proposal, not a decision
The order of the four layers and where the model sits: it proposes an SQL candidate after the semantic layer; validation, execution and evidence are decided in code.

Semantic layer: binding terms to contracts

A domain pack maps business terms and synonyms to declared metrics, dimensions and concrete columns. The Semantic Resolver interprets terms only within the selected data scope, and ambiguous values stop at a gate. Since July 9, 2026 the pack's standard table names are mapped to the customer's real tables, and since July 19 the pack's metric definitions are injected by the server directly into the SQL syntax tree. That was the turning point from asking the model to enforcing a contract in the compiler.

Validation layer: rejecting in the syntax tree

Generated SQL is parsed with sqlglot and inspected as a tree. Only a single SELECT is allowed. Insert, Update, Delete, Merge, Create, Drop, Alter, TruncateTable, Command and Copy nodes, multiple statements, unresolved Placeholder/Parameter nodes and anonymous functions outside the allow-list are rejected before execution. The code comments also say plainly that syntax checks complement semantic checks and do not establish that a query answers the right question.

Rejecting in the syntax treeIllustrative example — not a customer querySelectColumnsFromWhereGroupCOUNT(*)order_monthordersscope filterorder_monthA single SELECT only · passes here before the execution layerRejected before executionInsertUpdateDeleteMergeCreateDropAlterTruncateTableCommandCopymultiple statementsunresolved Placeholder / Parameteranonymous functions outside the allow-list→ all rejected before executionSyntax checks complement semantic checks; they do not establish that a query answers the right question (as the code comments say).
The tree parsed with sqlglot is inspected; statement kinds other than a single SELECT and unresolved parameters are rejected before execution (sql/validator.py _FORBIDDEN_NODES).

Execution layer: read-only, caps, rechecks

The approval route checks ownership and prior validation, then rechecks live permissions; a changed organization context or revoked grant stops even a prepared query. The collector connects with a read-only account, sets a session statement_timeout, and refuses to run when the EXPLAIN-estimated cost or row count exceeds the cap. Adapters without EXPLAIN fall back to timeout and LIMIT. There are five adapter families: PostgreSQL, MariaDB/MySQL, SQL Server, Oracle (including 11g) and SAP HANA.

Evidence layer: tying numbers to results

Figures in an answer are checked against returned values and units (numeric grounding). Audit records keep the question, query, approval and execution context, and per-stage timings — generation, model attempts, queue wait, composition — measured since August 17, 2026 and used as the baseline for field tuning.

What the field taught

On July 23 an experiment adding few-shot examples ended with zero improvement in five live checks and was closed with a record. On September 12 a defect that bound count questions to sum metrics, blocking a correct COUNT query, was fixed with a count-intent gate; on September 16 came the rule that totals are neither units nor business terms. On September 3 we found 6,460 column descriptions with Korean business terms replaced by English abbreviations, stopped, and escalated to the owner. Accuracy came from meaning definitions and data quality, not from the model.

Five months in the field: the days the design changed2026-06-03engine repositorybegins07-09pack table namesmapped to customertables07-19metric definitionsinjected into thesyntax tree07-23few-shot track closed0 gain in 5 checks07-28first customer PoC08-12first fielddeployment08-31Oracle universityrecords DBconnected09-036,460 columndescriptions found→ stopped, escalated09-12count-intent gatefixes sum-bindingdefect11-07first real-customerDB freeze reportedIn date order · spacing is not to scale · dates from repository history
Design turning points and field deployments recorded in repository history, June to November 2026.

The service opened on this engine: QueryMind

First customer PoC on July 28, 2026; first field deployment on August 12; an Oracle-based university records database connected on August 31; the first real-customer database freeze reported on November 7. The same engine runs in two different worlds, manufacturing ERP and university records, and approved query definitions are published as MCP tools called from Claude and ChatGPT. The repository has accumulated 3,483 commits since June 3.

QueryMind workspace capture
QueryMind workspace · separate foundation demo, Korean UI (2026-11-08, aggregate values from the demo environment).