Describe what a column's type leaves out, and say what the provider said - #32
Merged
Merged
Conversation
Listing models swallowed every HTTP error and returned an empty list, so a rejected key, a rate limit and an outage were indistinguishable from a provider with no models. The provider's own sentence now reaches the reader. Of the thirteen models one provider lists, six cannot hold a conversation, and the list is alphabetical, so a broken one sat where the user picks: the query then failed after the model list had looked healthy. Speech, transcription and classifier models are no longer offered. Reasoning models narrate before they answer. That monologue was shown as the query's description, and through Explain and the schema answers, which never pass through the SQL extractor. It is now removed at the client boundary and filtered out of the token stream as it arrives, so a streaming host no longer forwards it live. A tag that appears inside an answer is left alone. A hosted provider configured with a local base URL is refused rather than sent the API key. Leaving a local provider's base URL in place made "hosted" requests go to this machine, answer without a key, and report success; the stale setting is now cleared when settings open. The refusal never echoes the URL, which can embed credentials. Settings ask for the key before the model, so fetching models has what it needs, and Test Provider is renamed Test Connection and runs once a model is chosen.
A MongoDB document can use a map where the keys are values: `{ owed: { "ada@example.com": 120 } }`.
Walked as fields, every address became a column NAME, and a name is not removed by the cell-value
opt-in, which strips sampled values only. Those names reached the prompt on the default path.
A record repeats its fields across documents; a map does not, so a parent whose children do not
recur is described by its shape instead. The judgement is per child, so a summary field beside the
keys keeps its name, and shape decides before reuse, so an address goes whatever its frequency.
Four shapes needed separating from a real record and are covered by tests: a root-level map, a key
containing dots whose first segment matches a real field, a map nested in an array element, and a
polymorphic sub-document whose fields are mutually exclusive. Field names that are not ASCII are
kept, and a collection holding one document per integration keeps all of its names.
A column type says nothing about two things a query depends on, and both produced answers that were wrong with no error raised. An integer column holding a moment carries no unit. Comparing epoch milliseconds against epoch seconds matches every row, and against a text date matches none. The unit is now read from the column's range and stated in the schema; a range spanning two units, or a sentinel such as a "never expires" maximum, yields no hint rather than a wrong one. A JSON column carries no key names, so the model invented one and matched nothing. The schema now names the accessor for the engine and how many keys recur. The key NAMES ride the existing cell-value opt-in: a map with a stable key set is structurally identical to a record, so no threshold separates them and the names are treated as data. Lists are described by their element type and the membership test that engine actually accepts. A filter comparing a column against a value the column does not hold is confirmed against the database and reported, instead of answering zero as though nothing matched. Only a literal that decides the result counts: under OR, NOT, CASE or a partial IN the query returns rows, and a caveat there would contradict the answer beside it. Identifiers, moments and measurements are left alone, views are never probed, and the values are named to the model only under the same opt-in. The judgement is shared: one module per language, with each engine supplying only its own probe SQL and accessor. Both are held to one set of golden vectors, so the two implementations cannot drift apart the way they already had. Probes are bounded per table and per query, carry a timeout on every engine, and never overwrite a comment the schema already had.
Two reviews rejected the extension under "Product is Testable" with identical wording. The notes now lead with a temporary provider key and a two-minute path that needs no database of the reviewer's own: save six lines as a CSV, add it as a connection, ask one question, and check the three numbers it returns. What to do without a key at all is stated too, since most of the UI is exercisable offline. They also named a model the provider has since retired, which answers 404. A reviewer following the instructions would have concluded the product does not work. The notes now name a model that exists and carry a one-line check to run before submitting, because any provider can retire one at any time.
npm: core 0.9.0, sqlite 0.6.0, postgres 0.4.0, mysql 0.4.0, duckdb 0.4.0, oracle 0.4.0, mongodb 0.3.0, server 0.6.3. Minor across the engine packages: a JSON hint now takes the cell-value opt-in, the epoch classifier reads a range rather than one end, and the default output changed. VS Code 0.8.0, browser extension 0.4.0 and JetBrains 0.6.0 are bumped by hand: the first two bundle the engine, and the plugin carries its own provider and settings work.
The plugin's introspectors stamp an integer moment column with the unit it holds, but the prompt never said what to do with that, so the unit was named and then ignored: milliseconds compared against a seconds bound match every row. PostgreSQL, MySQL, DuckDB and Oracle now carry the same note the engine packages do; SQLite already said it at greater length. The golden vector is regenerated with it. The parity job caught this, and the local run did not, because the vector on disk was still the old one.
rahulmahadik
temporarily deployed
to
github-pages
August 20, 2026 03:48 — with
GitHub Actions
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five commits. Three fix wrong answers a user could not tell were wrong, one closes a privacy gap, one
rewrites the notes a store reviewer is handed.
What a column's type leaves out
An integer column holding a moment carries no unit, and a JSON column carries no key names. Both gaps
produced answers that were wrong with no error raised: epoch milliseconds compared against epoch
seconds matches every row, against a text date matches none, and a guessed JSON key matches nothing.
The schema now states the unit, read from the column's range rather than one end, and names the
accessor that engine actually accepts. A range spanning two units, or a sentinel such as a "never
expires" maximum, yields no hint rather than a wrong one.
A filter comparing a column against a value the column does not hold is confirmed against the
database and reported, instead of answering zero as though nothing matched. Only a literal that
decides the result counts: under OR, NOT, CASE or a partial IN the query returns rows and a caveat
there would contradict the answer beside it.
The judgement is one module per language, shared by every engine, with each engine supplying only its
own probe SQL and accessor. Both are held to one set of 41 golden vectors, because the two
implementations had already drifted.
The keys were the data
A document may use a map where the keys are data:
{ owed: { "ada@example.com": 120 } }. Walkednaively, every address became a column NAME, and the data opt-in never removed it, because that
switch strips sampled values only. So addresses reached the model on the default path, against what
PRIVACY.md promises. A record repeats its keys across documents and a map does not, so the shape now
decides, judged per child. The same reasoning gates the JSON key names above: a map with a stable key
set is structurally identical to a record, so those names ride the existing cell-value opt-in.
A hosted provider configured with a loopback base URL now refuses rather than sending the API key in
cleartext to whatever is listening on that port. Three of the four surfaces did the latter.
What the provider said
A failing provider reported a generic message, so a retired model, a wrong key and a regional
outage were indistinguishable. The provider's own words are shown, the model list offers only models
that answer, and a reasoning model's private
<think>monologue no longer leaks into the answer orthe explanation.
Verification
3136 TS tests, 806 Kotlin, both suites green, plus live runs against SQLite, Postgres, MySQL, DuckDB,
Oracle and MongoDB. Measured on real schemas: MySQL 0/4 to 3/4, Postgres 2/5 to 4/5, Room 5/6.
Versions: core 0.9.0, sqlite 0.6.0, postgres/mysql/duckdb/oracle 0.4.0, mongodb 0.3.0, server 0.6.3,
VS Code 0.8.0, browser extension 0.4.0, JetBrains 0.6.0.