From fe71ac88efb2279befb00a1ed3c5ec6f126edcb5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 13:55:35 +0000 Subject: [PATCH 1/4] fix(driver-sql,driver-turso): the aggregate refusals say what was decided without a tracker number The three aggregate refusals stage 1 held (undeclared function, count_distinct without a field, per-aggregation filter) drop their citation on both faces in one commit, so the byte-parity tests between driver-sql and the Turso remote transport keep comparing one sentence. The remote transport's declared-but-uncompiled and date-bucket refusals drop theirs too, which makes them equal to driver-sql's copies again. Each sentence already stated the decision, so only the citation goes. The prose-id ledger is recomputed with --census-ledger: the driver-sql row leaves, and remote-transport.ts goes from 40 to 35 occurrences. Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude --- packages/drivers/driver-sql/src/sql-driver.ts | 6 +++--- packages/drivers/driver-turso/src/remote-transport.ts | 10 +++++----- scripts/doc-authoring-prose-id.baseline.json | 9 --------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/packages/drivers/driver-sql/src/sql-driver.ts b/packages/drivers/driver-sql/src/sql-driver.ts index 824a3fb801c..0af59824882 100644 --- a/packages/drivers/driver-sql/src/sql-driver.ts +++ b/packages/drivers/driver-sql/src/sql-driver.ts @@ -1696,7 +1696,7 @@ function undeclaredAggregateFunctionError(func: string): Error { `Declared functions: ${DECLARED_AGGREGATE_FUNCTIONS.join(', ')} ` + `(@objectstack/spec AggregationFunction). Fix the "function" key of the aggregations[] ` + `entry — the Query Protocol has no such function, so this is a query no backend can run, ` + - `not a gap in this one (#5907).`, + `not a gap in this one.`, ) as Error & { code?: string; status?: number }; err.code = StandardErrorCode.enum.INVALID_QUERY; err.status = 400; @@ -1800,7 +1800,7 @@ function unsupportedAggregationFilterError(alias: string, backend: string): Erro `Per-aggregation \`filter\` on "${alias}" is not supported by this backend (${backend}). ` + `The query is spelled correctly and @objectstack/spec AggregationNodeSchema declares the key — ` + `this backend compiles no conditional-aggregate (SQL FILTER (WHERE …) / CASE WHEN) expression ` + - `for it, so it is refused rather than silently aggregating the UNFILTERED rows (#10413), which ` + + `for it, so it is refused rather than silently aggregating the UNFILTERED rows, which ` + `is why it answers NOT_IMPLEMENTED/501 rather than a 400. \`engine.aggregate\` lowers filtered ` + `aggregations in memory for every driver without native support — route the query through the ` + `engine, or drop the \`filter\` key.`, @@ -1834,7 +1834,7 @@ function refuseDistinctAggregateWithoutField(func: string): never { const err = new Error( `Aggregate function "${func}" needs a "field" — there is nothing to deduplicate. ` + `COUNT(*) counts rows and is the spelling that takes no field; a distinct count has to name ` + - `the column whose values are deduplicated. Add "field" to the aggregations[] entry (#6409).`, + `the column whose values are deduplicated. Add "field" to the aggregations[] entry.`, ) as Error & { code?: string; status?: number }; err.code = StandardErrorCode.enum.INVALID_QUERY; err.status = 400; diff --git a/packages/drivers/driver-turso/src/remote-transport.ts b/packages/drivers/driver-turso/src/remote-transport.ts index 1c7cb45f282..32f4a7461c3 100644 --- a/packages/drivers/driver-turso/src/remote-transport.ts +++ b/packages/drivers/driver-turso/src/remote-transport.ts @@ -945,7 +945,7 @@ function undeclaredAggregateFunctionError(func: string): Error { `Declared functions: ${DECLARED_AGGREGATE_FUNCTIONS.join(', ')} ` + `(@objectstack/spec AggregationFunction). Fix the "function" key of the aggregations[] ` + `entry — the Query Protocol has no such function, so this is a query no backend can run, ` + - `not a gap in this one (#5907).`, + `not a gap in this one.`, ) as Error & { code?: string; status?: number }; err.code = StandardErrorCode.enum.INVALID_QUERY; err.status = 400; @@ -984,7 +984,7 @@ function uncompilableAggregateFunctionError(func: string): Error { `correctly and @objectstack/spec AggregationFunction declares it — this is a capability gap ` + `in the backend, not a mistake in the query, which is why it answers NOT_IMPLEMENTED/501 ` + `rather than a 400. Aggregate with a function this backend compiles; whether the declaration ` + - `itself should stand is ADR-0049's enforce-or-remove question (#5907).`, + `itself should stand is ADR-0049's enforce-or-remove question.`, ) as Error & { code?: string; status?: number }; err.code = StandardErrorCode.enum.NOT_IMPLEMENTED; err.status = 501; @@ -1011,7 +1011,7 @@ function refuseDistinctAggregateWithoutField(func: string): never { const err = new Error( `Aggregate function "${func}" needs a "field" — there is nothing to deduplicate. ` + `COUNT(*) counts rows and is the spelling that takes no field; a distinct count has to name ` + - `the column whose values are deduplicated. Add "field" to the aggregations[] entry (#6409).`, + `the column whose values are deduplicated. Add "field" to the aggregations[] entry.`, ) as Error & { code?: string; status?: number }; err.code = StandardErrorCode.enum.INVALID_QUERY; err.status = 400; @@ -1068,7 +1068,7 @@ function refusePerAggregationFilter(alias: string): never { `Per-aggregation \`filter\` on "${alias}" is not supported by this backend (Turso remote transport). ` + `The query is spelled correctly and @objectstack/spec AggregationNodeSchema declares the key — ` + `this backend compiles no conditional-aggregate (SQL FILTER (WHERE …) / CASE WHEN) expression ` + - `for it, so it is refused rather than silently aggregating the UNFILTERED rows (#10413), which ` + + `for it, so it is refused rather than silently aggregating the UNFILTERED rows, which ` + `is why it answers NOT_IMPLEMENTED/501 rather than a 400. \`engine.aggregate\` lowers filtered ` + `aggregations in memory for every driver without native support — route the query through the ` + `engine, or drop the \`filter\` key.`, @@ -1105,7 +1105,7 @@ function refuseDateBucketedGroupBy(granularity: string): never { `a capability gap in the backend, not a mistake in the query, which is why it answers ` + `NOT_IMPLEMENTED/501 rather than a 400. A driver publishes the granularities it buckets ` + `natively as \`supports.queryDateGranularity\`; the engine reads that record and buckets ` + - `in memory for every granularity absent from it, which is always correct (#6212).`, + `in memory for every granularity absent from it, which is always correct.`, ) as Error & { code?: string; status?: number }; err.code = StandardErrorCode.enum.NOT_IMPLEMENTED; err.status = 501; diff --git a/scripts/doc-authoring-prose-id.baseline.json b/scripts/doc-authoring-prose-id.baseline.json index da4c5c61053..8c1cad44e9b 100644 --- a/scripts/doc-authoring-prose-id.baseline.json +++ b/scripts/doc-authoring-prose-id.baseline.json @@ -112,11 +112,6 @@ "packages/drivers/driver-mongodb/src/test-mongod.ts": { "#5517": 3 }, - "packages/drivers/driver-sql/src/sql-driver.ts": { - "#10413": 1, - "#5907": 1, - "#6409": 1 - }, "packages/drivers/driver-turso/src/remote-canonical-backfill.ts": { "#1005": 1, "#5770": 1 @@ -124,7 +119,6 @@ "packages/drivers/driver-turso/src/remote-transport.ts": { "#1003": 1, "#1004": 6, - "#10413": 1, "#1051": 1, "#1058": 6, "#1066": 3, @@ -138,10 +132,7 @@ "#5369": 1, "#5769": 3, "#5903": 1, - "#5907": 2, "#6050": 1, - "#6212": 1, - "#6409": 1, "#7929": 1, "#8197": 1 }, From ab16ca9224fb2b30b768ed40912641eb197554b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 13:56:50 +0000 Subject: [PATCH 2/4] fix(driver-turso): filter refusals and the remote auto_number refusal say what was decided without a tracker number The remote transport's filter refusals (the withheld wire sentences and the full diagnostics behind them) drop their citations. Most of those numbers were bare ids from the repository this file used to live in, so here they resolved to unrelated cards. Where the sentence already stated the decision, only the citation goes. Two gain words: the non-boolean $null diagnostic now says every driver refuses it, so one filter no longer gets a different answer per backend, and the remote auto_number refusal says it refuses rather than resolves because resolving would write NULL into the slot. Two pins move onto the sentence: the $null test asserts the ruling in words instead of three ids, and the unbindable-comparand test asserts 'indistinguishable from "no rows matched".' instead of two ids. The prose-id ledger is recomputed: the remote-transport.ts row leaves, and turso-driver.ts keeps one occurrence (the log line). Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude --- ...remote-transport-comparand-refusal.test.ts | 2 +- ...e-transport-null-comparand-refusal.test.ts | 9 ++-- .../driver-turso/src/remote-transport.ts | 43 +++++++++---------- .../drivers/driver-turso/src/turso-driver.ts | 3 +- scripts/doc-authoring-prose-id.baseline.json | 21 --------- 5 files changed, 29 insertions(+), 49 deletions(-) diff --git a/packages/drivers/driver-turso/src/remote-transport-comparand-refusal.test.ts b/packages/drivers/driver-turso/src/remote-transport-comparand-refusal.test.ts index 6a04eb5ff7e..4376a935601 100644 --- a/packages/drivers/driver-turso/src/remote-transport-comparand-refusal.test.ts +++ b/packages/drivers/driver-turso/src/remote-transport-comparand-refusal.test.ts @@ -231,7 +231,7 @@ describe('RemoteTransport comparand refusal — the value half of #1004', () => const { t } = transportWithCapturingClient(); const err = await t.find('deal', { where: { payload: { $eq: { a: 1 } } } }).catch((e) => e); expect(err.message).toMatch(/must be a string, number, bigint, boolean, null or Date/); - expect(err.message).toMatch(/#1004, #1058/); + expect(err.message).toContain('indistinguishable from "no rows matched".'); }); it('truncates a large comparand rather than pasting it into the log', async () => { diff --git a/packages/drivers/driver-turso/src/remote-transport-null-comparand-refusal.test.ts b/packages/drivers/driver-turso/src/remote-transport-null-comparand-refusal.test.ts index 5aadf1dd779..1005cac3859 100644 --- a/packages/drivers/driver-turso/src/remote-transport-null-comparand-refusal.test.ts +++ b/packages/drivers/driver-turso/src/remote-transport-null-comparand-refusal.test.ts @@ -174,11 +174,12 @@ describe('RemoteTransport $null comparand refusal (#1116)', () => { expect(err.message).toContain('the side opposite the false it was written to mean'); }); - it('cites the ruling and the framework landing so both repos are findable', async () => { + it('states the ruling and its landing in words: refused, not coerced, by every driver', async () => { const err = await refusalOf({ stage: { $null: 'yes' } }); - expect(err.message).toContain('objectstack#5347'); - expect(err.message).toContain('objectstack#5368'); - expect(err.message).toContain('#1116'); + expect(err.message).toContain('It is refused rather than coerced'); + expect(err.message).toContain( + 'Every driver now refuses it, so one filter no longer gets a different answer per backend.', + ); }); it('names the spec declaration rather than only complaining', async () => { diff --git a/packages/drivers/driver-turso/src/remote-transport.ts b/packages/drivers/driver-turso/src/remote-transport.ts index 32f4a7461c3..5a738eeb2d7 100644 --- a/packages/drivers/driver-turso/src/remote-transport.ts +++ b/packages/drivers/driver-turso/src/remote-transport.ts @@ -3853,8 +3853,8 @@ export class RemoteTransport { `(with driver-sql, driver-sqlite-wasm and Turso local) compiled IS NULL (anything but false), ` + `driver-memory's query path and driver-mongodb compiled IS NOT NULL (anything but true), and ` + `driver-memory's matcher dropped the constraint entirely. Note "false" the STRING is truthy, ` + - `so it landed on the side opposite the false it was written to mean ` + - `(objectstack#5347, objectstack#5368, #1116).`, + `so it landed on the side opposite the false it was written to mean. Every driver now ` + + `refuses it, so one filter no longer gets a different answer per backend.`, ); } @@ -3946,12 +3946,12 @@ export class RemoteTransport { `[RemoteTransport] Operator "$exists" on field "${field}" requires a boolean comparand (true or ` + `false). Received ${shown} (${preview(value)}) at '${object}.${field}'.$exists. ` + `@objectstack/spec FieldOperatorsSchema declares $exists as a boolean. It is refused rather ` + - `than coerced for the same reason $null is (objectstack#5347): a non-boolean lands on ` + + `than coerced for the same reason $null is: a non-boolean lands on ` + `whichever side the backend's two-branch conditional happens to default to, and those ` + `defaults point in OPPOSITE directions — this transport's \`=== false\` test compiled ` + `IS NOT NULL for anything but false, a \`=== true\` test compiles IS NULL for anything but ` + `true. Note "false" the STRING is truthy, so it landed on the side opposite the false it was ` + - `written to mean (objectstack#5369, objectstack#5903).`, + `written to mean.`, ); } @@ -4156,7 +4156,7 @@ export class RemoteTransport { subtree, `[RemoteTransport] $between on ${target} must be lowered to $gte/$lte before it reaches the ` + `transport — TursoDriver.toRemoteFieldSpec does that so the calendar-day upper-bound rule is ` + - `applied exactly once (#1003). Refusing rather than compiling a second, rule-free range.`, + `applied exactly once. Refusing rather than compiling a second, rule-free range.`, ); } // [#20020, the #8220 contract] The two arms below name the target and the @@ -4182,7 +4182,7 @@ export class RemoteTransport { subtree, `[RemoteTransport] Unsupported filter operator "${op}" on ${target} in remote mode. Supported: ` + `${SUPPORTED_FILTER_OPERATORS.join(', ')}. Refusing rather than compiling it to an equality — a ` + - `silent degradation is indistinguishable from "no rows matched" (#1004).`, + `silent degradation is indistinguishable from "no rows matched".`, ); } @@ -4239,7 +4239,7 @@ export class RemoteTransport { subtree, `${shared}. "${key}" IS a field operator, one level down: write ` + `{ : { "${key}": } } — e.g. { stage: { "${key}": … } } — rather than putting ` + - `it at the condition level (objectstack#5769, objectstack#5348).`, + `it at the condition level.`, ); } return this.withheldRefusal( @@ -4247,8 +4247,7 @@ export class RemoteTransport { subtree, `${shared}. The Filter Protocol declares no "${key}" at any level; on a read it cost an empty ` + `page, and on deleteMany/updateMany the predicate is constantly false or constantly true ` + - `depending on which side of the comparison the literal lands (objectstack#5769, ` + - `objectstack#5348).`, + `depending on which side of the comparison the literal lands.`, ); } @@ -4291,7 +4290,7 @@ export class RemoteTransport { `filter conditions, but received ${shown} (${preview(value)}). @objectstack/spec ` + `FilterConditionSchema declares "${key}" as FilterCondition[]. Refusing rather than falling ` + `through to the field path, which read '${key}' as a COLUMN NAME and compiled a predicate ` + - `against a column that cannot exist (objectstack#5769).`, + `against a column that cannot exist.`, ); } @@ -4439,7 +4438,7 @@ export class RemoteTransport { `because the backends disagreed: driver-sql handed it to knex and got a bare "Undefined ` + `binding(s)" Error carrying no code, while Turso's remote transport compiled it to IS NULL ` + `— so \`{ owner_id: ctx.user?.id }\` with a missing id silently matched every env-wide row ` + - `instead of failing (#6050).`, + `instead of failing.`, ); } @@ -4490,12 +4489,12 @@ export class RemoteTransport { `FilterConditionSchema declares \`$not: FilterConditionSchema\`). Refusing rather than ` + `reading '${branch}' as a COLUMN NAME — that is the bug this branch exists to close: it ` + `compiled a predicate against a column that cannot exist, and named it as a field of ` + - `'${object}' when it complained (#1051, #1076). A negation of "no condition" is written ` + + `'${object}' when it complained. A negation of "no condition" is written ` + `\`{ $not: {} }\`, which matches zero rows.` : `Every element of ${branch} must be a plain object of conditions (spec ` + `FilterConditionSchema). Refusing rather than skipping it — a dropped element leaves ` + - `valid SQL that answers a DIFFERENT question: narrower in $or, wider in $and (#1004, ` + - `#1058, #1066, #1073). An intentionally unconstrained branch is written \`{}\`.`; + `valid SQL that answers a DIFFERENT question: narrower in $or, wider in $and. An ` + + `intentionally unconstrained branch is written \`{}\`.`; // [#20039, the #8220 contract] The position, the kind and the preview are // the predicate's (a policy's literal, measured) — and so is which of the // three combinators it sat under, since the requirement sentence names it. @@ -4562,8 +4561,8 @@ export class RemoteTransport { `[RemoteTransport] where on '${object}' is ${describeValue(filters)}, not a filter condition: ` + `${preview(filters)}. ${requirement} Refusing rather than compiling it to NO WHERE clause — ` + `a filter that vanishes WIDENS the statement to the whole table, so a read returns exactly ` + - `the rows the filter was written to exclude and deleteMany/updateMany touch every row ` + - `(#1004, #1058, #1066, #1073, #1075). "No filter" is written \`{}\`, or by omitting \`where\`.`, + `the rows the filter was written to exclude and deleteMany/updateMany touch every row. ` + + `"No filter" is written \`{}\`, or by omitting \`where\`.`, ); } @@ -4601,7 +4600,7 @@ export class RemoteTransport { `(${SUPPORTED_FILTER_OPERATORS.join(', ')}); to compare against a value, write the value ` + `itself. Refusing rather than dropping the condition — a predicate that vanishes WIDENS the ` + `statement to the whole table, so the caller gets back exactly the rows the filter was ` + - `written to exclude (#1004, #1058, #1066).`, + `written to exclude.`, ); } @@ -4687,9 +4686,9 @@ export class RemoteTransport { `compares a column against another column instead of against a value. The query DSL ` + `declares this form (spec FieldReferenceSchema) but no executor compiles it, so it is ` + `refused here rather than bound as the marker's JSON text — which is valid SQL that ` + - `matches nothing (#1058). Compare against a literal, or select both columns and compare ` + + `matches nothing. Compare against a literal, or select both columns and compare ` + `after retrieval. The columns and the operator this filter used are withheld from the ` + - `message (#7929); the full diagnostic is in the server log.`, + `message; the full diagnostic is in the server log.`, diagnostic, value, `${diagnostic} No executor compiles this form in remote mode — compare against a ` + @@ -4712,14 +4711,14 @@ export class RemoteTransport { `transport cannot bind. A comparison value must be ` + `${ACCEPTED_FILTER_COMPARAND_TYPES_SENTENCE}. Refusing rather than binding its JSON text ` + `— that compiles to valid SQL matching zero rows, which is indistinguishable from ` + - `"no rows matched" (#1004, #1058).`; + `"no rows matched".`; this.diagnosticSink?.(diagnostic); return withheldInvalidFilterError( `[RemoteTransport] A filter comparand in this query is a value this transport cannot bind. ` + `A comparison value must be ${ACCEPTED_FILTER_COMPARAND_TYPES_SENTENCE}. Refusing rather ` + `than binding its JSON text — that compiles to valid SQL matching zero rows, which is ` + - `indistinguishable from "no rows matched" (#1004, #1058). The field, the operator and the ` + - `value this filter used are withheld from the message (#8197); the full diagnostic is in ` + + `indistinguishable from "no rows matched". The field, the operator and the ` + + `value this filter used are withheld from the message; the full diagnostic is in ` + `the server log.`, diagnostic, value, diff --git a/packages/drivers/driver-turso/src/turso-driver.ts b/packages/drivers/driver-turso/src/turso-driver.ts index 61ba83b2cbd..3835c97bc7b 100644 --- a/packages/drivers/driver-turso/src/turso-driver.ts +++ b/packages/drivers/driver-turso/src/turso-driver.ts @@ -317,7 +317,8 @@ function refuseRemoteAutonumber(object: string, fields: string[], path: string): `which is why it answers NOT_IMPLEMENTED/501 rather than a 400. Use the local or ` + `embedded-replica transport for objects that carry record numbers, or supply the value ` + `explicitly (a seed replay or \`preserveAudit\` import keeps its own numbers and is written ` + - `unchanged). Until this change the same call RESOLVED and wrote NULL into the slot (#6944).`, + `unchanged). It is refused rather than resolved, because resolving would write NULL into the ` + + `slot and persist the row without its record number.`, ) as Error & { code?: string; status?: number }; err.code = StandardErrorCode.enum.NOT_IMPLEMENTED; err.status = 501; diff --git a/scripts/doc-authoring-prose-id.baseline.json b/scripts/doc-authoring-prose-id.baseline.json index 8c1cad44e9b..829c3d44367 100644 --- a/scripts/doc-authoring-prose-id.baseline.json +++ b/scripts/doc-authoring-prose-id.baseline.json @@ -116,28 +116,7 @@ "#1005": 1, "#5770": 1 }, - "packages/drivers/driver-turso/src/remote-transport.ts": { - "#1003": 1, - "#1004": 6, - "#1051": 1, - "#1058": 6, - "#1066": 3, - "#1073": 2, - "#1075": 1, - "#1076": 1, - "#1116": 1, - "#5347": 2, - "#5348": 2, - "#5368": 1, - "#5369": 1, - "#5769": 3, - "#5903": 1, - "#6050": 1, - "#7929": 1, - "#8197": 1 - }, "packages/drivers/driver-turso/src/turso-driver.ts": { - "#6944": 1, "#7099": 1 }, "packages/formula/src/matches-filter.ts": { From 8c75416558c50c7ca22f3d982fc09646a1278429 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 13:57:40 +0000 Subject: [PATCH 3/4] fix(driver-turso): the three log lines say what was decided without a tracker number The unnumbered-upsert warning drops its citation; it already said what an operator should do. The remote canonical backfill's info line now says what a conversion buys (the converted rows hold the one canonical text form, and the column drops the unindexable read-side repair only once a pass finds nothing left to convert). The unresolvable-remainder warning replaces its cross-repo citation with the rule: a value that cannot be read as an instant is counted and reported, never guessed at. The prose-id ledger is recomputed: the last two driver-turso rows leave, so the package holds no ledgered id. Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude --- .../drivers/driver-turso/src/remote-canonical-backfill.ts | 8 +++++--- packages/drivers/driver-turso/src/turso-driver.ts | 4 ++-- scripts/doc-authoring-prose-id.baseline.json | 7 ------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/drivers/driver-turso/src/remote-canonical-backfill.ts b/packages/drivers/driver-turso/src/remote-canonical-backfill.ts index dcbb875a6bf..afeb6bcb02a 100644 --- a/packages/drivers/driver-turso/src/remote-canonical-backfill.ts +++ b/packages/drivers/driver-turso/src/remote-canonical-backfill.ts @@ -810,7 +810,9 @@ export async function backfillRemoteCanonicalColumns( ); } else if (report.rowsConverted || report.epochTextRowsConverted) { logger?.info?.( - `[driver-turso] canonicalised remote ${column.kind} storage (#5770) for ${where}`, + `[driver-turso] canonicalised remote ${column.kind} storage for ${where}: the converted ` + + `rows now hold the one canonical text form, and the column drops the unindexable ` + + `read-side repair only once a pass finds nothing left to convert`, { rowsConverted: report.rowsConverted, epochTextRowsConverted: report.epochTextRowsConverted, @@ -833,8 +835,8 @@ export async function backfillRemoteCanonicalColumns( if (report.unresolvedEpochTextRows > 0) { logger?.warn( `[driver-turso] ${report.unresolvedEpochTextRows} row(s) in ${where} hold digits-only ` + - `text outside the interpretable epoch-millisecond band and were left untouched ` + - `(cloud#1005 后果 B, unresolvable remainder)`, + `text outside the interpretable epoch-millisecond band and were left untouched: a value ` + + `that cannot be read as an instant is counted and reported, never guessed at`, { table: column.table, field: column.field }, ); } diff --git a/packages/drivers/driver-turso/src/turso-driver.ts b/packages/drivers/driver-turso/src/turso-driver.ts index 3835c97bc7b..51449750322 100644 --- a/packages/drivers/driver-turso/src/turso-driver.ts +++ b/packages/drivers/driver-turso/src/turso-driver.ts @@ -2358,8 +2358,8 @@ export class TursoDriver extends SqlDriver { `not generate record numbers, so an upsert that matches no existing row inserts one without ` + `it — the row is persisted, its record number is not, and nothing else reports this. Supply ` + `the value explicitly on this path (a seed replay or import keeps its own numbers and is ` + - `written unchanged), or use the local / embedded-replica transport, which do issue them ` + - `(#7099).`, + `written unchanged), or use the local / embedded-replica transport, which do issue ` + + `them.`, { object, fields: unfilled, id: row.id, path }, ); } diff --git a/scripts/doc-authoring-prose-id.baseline.json b/scripts/doc-authoring-prose-id.baseline.json index 829c3d44367..3c1fd9c4ef0 100644 --- a/scripts/doc-authoring-prose-id.baseline.json +++ b/scripts/doc-authoring-prose-id.baseline.json @@ -112,13 +112,6 @@ "packages/drivers/driver-mongodb/src/test-mongod.ts": { "#5517": 3 }, - "packages/drivers/driver-turso/src/remote-canonical-backfill.ts": { - "#1005": 1, - "#5770": 1 - }, - "packages/drivers/driver-turso/src/turso-driver.ts": { - "#7099": 1 - }, "packages/formula/src/matches-filter.ts": { "#5240": 1 }, From 252a378f9908e75d931168a222b3c868e74ea2e4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 13:58:02 +0000 Subject: [PATCH 4/4] chore(changeset): driver-turso and driver-sql runtime strings state the decision Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude --- ...urso-runtime-strings-state-the-decision.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .changeset/20513-driver-turso-runtime-strings-state-the-decision.md diff --git a/.changeset/20513-driver-turso-runtime-strings-state-the-decision.md b/.changeset/20513-driver-turso-runtime-strings-state-the-decision.md new file mode 100644 index 00000000000..1f9a348068b --- /dev/null +++ b/.changeset/20513-driver-turso-runtime-strings-state-the-decision.md @@ -0,0 +1,33 @@ +--- +'@objectstack/driver-turso': patch +'@objectstack/driver-sql': patch +--- + +driver-turso refusals and log lines, and driver-sql's last three aggregate refusals, no longer cite tracker numbers; each states the reason in words + +Clause-②: no + +Many messages the Turso driver shows to authors and operators ended with an issue-tracker number where +the reason belonged. Most of the Turso remote transport's numbers were bare ids from the repository that +file used to live in, so here they pointed at unrelated cards. The number goes, and where the sentence +did not already say what was decided, it now does: + +- Aggregate refusals, on both drivers: the undeclared-function, `count_distinct`-without-`field` and + per-aggregation `filter` refusals lose their citation on the SQL driver and the Turso remote + transport together, so the two faces still read one sentence. The remote transport's + declared-but-uncompiled and date-bucket refusals lose theirs too, and read exactly like the SQL + driver's again. +- Turso remote filter refusals (`INVALID_FILTER`): the withheld cross-field and unbindable-comparand + wording, and the full diagnostics behind every filter refusal (unsupported operator, unlowered + `$between`, undeclared or non-list combinator, non-node operand, non-object `where`, empty operator + map, undefined comparand, non-boolean `$exists`) lose only the citation, because their sentences + already said it. The non-boolean `$null` diagnostic now says every driver refuses it, so one filter + no longer gets a different answer per backend. +- The Turso remote `auto_number` refusal (`NOT_IMPLEMENTED`) now says why it refuses rather than + resolves: resolving would write NULL into the slot and persist the row without its record number. +- Log lines: the unnumbered-upsert warning loses its citation; the remote canonical backfill's info line + says what a conversion buys (the column drops the unindexable read-side repair only once a pass finds + nothing left to convert); the unresolvable-remainder warning says a value that cannot be read as an + instant is counted and reported, never guessed at. + +Text only: no error code, field name, status or behaviour changes.