You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] driver-turso puts one declared Field.json in two different physical column types — json on the local transport, TEXT on the remote one — and nothing records that as intended #12586
Filed by the domain:engine PM seat on behalf of the os-dev seat that implemented #12393 (PR #12585). That seat could not file it: direct REST to the GitHub API returns 403 from a dev seat (the #12123 gap, documented on #12380) and MCP issue search is off-limits for dedup reads under the dispatch contract, so filing would have been filing blind. It correctly refused and handed it up. The local-grep half of the evidence is theirs; the open-issue half is mine.
The measurement
One driver, two transports, and the same declared field lands in a different physical column type on each:
transport
what a Field.json column is declared as
local (SqlDriver)
json — confirmed from the catalog by #12380's suite
remote (RemoteTransport.mapFieldTypeToSQL)
TEXT — returned for both json and multiple: true
Why it is worth a card while nothing is broken
It round-trips faithfully on both transports today. PR #12585's new VALUE_ROUNDTRIP case-set measures exactly that, on both, and both answered green on arrival.
What makes it a finding is what the ablation showed. Restoring the pre-#12380 SQLite json branch broke the two transports differently — 31 failures across the driver where a symmetric defect would have produced a symmetric count. The two faces diverge by exactly one case (s_0123), and the mechanism is the column type:
local emits a column declared json, so SQLite's NUMERIC affinity destroys the bare string '0123' before storage;
remote emits TEXT, where no affinity conversion applies, JSON.parse('0123') throws, and the keep-as-string fallback holds.
So the physical divergence is not cosmetic — it changes what a value becomes on disk, and therefore which defects each transport is exposed to. That is the #11535 class: two paths that agree on the happy path and disagree the moment anything perturbs them. Today the disagreement is masked because both happen to be correct; the next codec change has no reason to be kind to both.
What is missing, precisely
Not the behaviour — the declaration. A local grep finds only the two mapFieldTypeToSQL lines and a single comment in remote-read-coercion.test.ts (// json stored as TEXT). Nothing anywhere states the asymmetry as a known, intended fact, with the reason it is safe. So a reader has no way to tell a deliberate design from an oversight, and neither does a future change: there is no pin that would go red if one side moved.
Possible dispositions — none chosen here
Converge them — emit the same declared type on both transports. Cleanest, and the one that removes the class rather than documenting it; needs measuring against whatever made remote choose TEXT in the first place.
Declare and pin the asymmetry — record why each side is what it is, and add a pin that fails if either changes without the other. Cheapest honest option.
Leave it — defensible only if someone writes down why, which is the thing that is currently absent.
Whichever way it goes, the VALUE_ROUNDTRIP case-set from #12393 now covers both transports, so a convergence attempt has an instrument to check itself against — which it did not have before today.
Filed by the
domain:enginePM seat on behalf of theos-devseat that implemented #12393 (PR #12585). That seat could not file it: direct REST to the GitHub API returns 403 from a dev seat (the #12123 gap, documented on #12380) and MCP issue search is off-limits for dedup reads under the dispatch contract, so filing would have been filing blind. It correctly refused and handed it up. The local-grep half of the evidence is theirs; the open-issue half is mine.The measurement
One driver, two transports, and the same declared field lands in a different physical column type on each:
Field.jsoncolumn is declared asSqlDriver)json— confirmed from the catalog by #12380's suiteRemoteTransport.mapFieldTypeToSQL)TEXT— returned for bothjsonandmultiple: trueWhy it is worth a card while nothing is broken
It round-trips faithfully on both transports today. PR #12585's new
VALUE_ROUNDTRIPcase-set measures exactly that, on both, and both answered green on arrival.What makes it a finding is what the ablation showed. Restoring the pre-#12380 SQLite
jsonbranch broke the two transports differently — 31 failures across the driver where a symmetric defect would have produced a symmetric count. The two faces diverge by exactly one case (s_0123), and the mechanism is the column type:json, so SQLite's NUMERIC affinity destroys the bare string'0123'before storage;TEXT, where no affinity conversion applies,JSON.parse('0123')throws, and the keep-as-string fallback holds.So the physical divergence is not cosmetic — it changes what a value becomes on disk, and therefore which defects each transport is exposed to. That is the #11535 class: two paths that agree on the happy path and disagree the moment anything perturbs them. Today the disagreement is masked because both happen to be correct; the next codec change has no reason to be kind to both.
What is missing, precisely
Not the behaviour — the declaration. A local grep finds only the two
mapFieldTypeToSQLlines and a single comment inremote-read-coercion.test.ts(// json stored as TEXT). Nothing anywhere states the asymmetry as a known, intended fact, with the reason it is safe. So a reader has no way to tell a deliberate design from an oversight, and neither does a future change: there is no pin that would go red if one side moved.Possible dispositions — none chosen here
TEXTin the first place.Whichever way it goes, the
VALUE_ROUNDTRIPcase-set from #12393 now covers both transports, so a convergence attempt has an instrument to check itself against — which it did not have before today.Dedup
mapFieldTypeToSQLlines and the oneremote-read-coercion.test.tscomment; no card, doc, changeset or ADR states the asymmetry.Filed unassigned, recording only — not claimed.