Skip to content

Commit 3fdd44c

Browse files
committed
docs(#16019): delta review — the publish door is /packages/publish, the degrade notes exclude turso remote, the card is cited by number
Text only: the changeset named the dispatcher's install route as the moving door and cited the follow-up card anonymously; the two degrade docblocks claimed the declared answer for driver-turso's remote transport, whose typed exits are undeclared and pre-date this card; the catch comment below them still said a declared 5xx is served through the ANALYTICS_QUERY_FAILED path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
1 parent d7d47a2 commit 3fdd44c

2 files changed

Lines changed: 17 additions & 8 deletions

File tree

.changeset/driver-raw-statement-declared-fault.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111

1212
- `POST /api/v1/analytics/dataset/query`: a driver fault on the raw path answers `500 {"code":"DATABASE_ERROR","error":"Internal server error"}` — the declared-fault relay, the same answer the `/data` door and `/analytics/query` already give a declared 5xx — where it was `500 {"code":"ANALYTICS_QUERY_FAILED","error":"Internal server error"}` when the phrasing heuristic happened to fire and the raw engine text when it did not. Status unchanged; the code is now the producer's, exactly as the typed read exits' faults have answered at this door since PR #9273.
1313
- The same door, a dataset over a backing table that is NOT present, on the native-SQL strategy (the strategy every deployment whose data engine exposes `execute()` runs): `500 DATABASE_ERROR` where it was `200 {"rows":[],"fields":[],"totals":[]}` plus a `warn`. `queryDataset`'s missing-source degrade sits behind its declared-envelope re-throw (#5717 defence B: a declared envelope is re-thrown untouched, whatever it says), so a driver-raised missing table no longer reaches it — the answer the ObjectQL-aggregate strategy has given since #9273, now on both strategies. The degrade still applies to an undeclared producer (an embedder's own `executeRawSql`, the framework's not-registered signals).
14-
- `POST /api/v1/packages` and `DELETE /api/v1/packages/:id`: a raw-exec driver fault under `sys_packages` answers `500 {"code":"DATABASE_ERROR"}` with the composed sentence as its message — `PackageService.publish` / `delete` re-throw a throw that declares an HTTP answer (`declaresHttpAnswer`, whose docblock already says a declared 5xx is re-thrown too) and the door's `sendThrownError` relays it — where it was `500 PACKAGE_PUBLISH_FAILED` / `500 PACKAGE_DELETE_FAILED` from the swallowing branch. Same status band, no dialect text on the wire either way; the ledgered `code` on those two doors moves.
14+
- `POST /api/v1/packages/publish` and `DELETE /api/v1/packages/:id`: a raw-exec driver fault under `sys_packages` answers `500 {"code":"DATABASE_ERROR"}` with the composed sentence as its message — `PackageService.publish` / `delete` re-throw a throw that declares an HTTP answer (`declaresHttpAnswer`, whose docblock already says a declared 5xx is re-thrown too) and the door's `sendThrownError` relays it — where it was `500 PACKAGE_PUBLISH_FAILED` / `500 PACKAGE_DELETE_FAILED` from the swallowing branch. Same status band, no dialect text on the wire either way; the ledgered `code` on those two doors moves.
1515

16-
**What a consumer of `execute()` sees.** `error.message` is the composed sentence; `error.code` is `DATABASE_ERROR` where it was the backend's errno; `error.status` is `500` where it was absent. The backend's error object — its errno, its diagnostic, and on the dialects that inline them the bound literals — is on `error.cause` (non-enumerable, so it does not serialise), and the driver writes it, with the statement, to its warn log before composing. Cause-following predicates are unaffected: `isMissingTableError(err, readObject)` still classifies a missing table raised on this path. An error that already declares a `status` is passed through untouched, never double-wrapped. A caller that read the dialect's text off `error.message` (a migration preflight recording it as its `detail`, say) now reads the composed sentence there and finds the dialect text on `cause` and in the log; the in-repo sites of that class are tracked as one follow-up card (read `cause` there).
16+
**What a consumer of `execute()` sees.** `error.message` is the composed sentence; `error.code` is `DATABASE_ERROR` where it was the backend's errno; `error.status` is `500` where it was absent. The backend's error object — its errno, its diagnostic, and on the dialects that inline them the bound literals — is on `error.cause` (non-enumerable, so it does not serialise), and the driver writes it, with the statement, to its warn log before composing. Cause-following predicates are unaffected: `isMissingTableError(err, readObject)` still classifies a missing table raised on this path. An error that already declares a `status` is passed through untouched, never double-wrapped. A caller that read the dialect's text off `error.message` (a migration preflight recording it as its `detail`, say) now reads the composed sentence there and finds the dialect text on `cause` and in the log; the in-repo sites of that class are tracked as #16657 (read `cause` there).

packages/services/service-analytics/src/analytics-service.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,15 @@ function isMissingColumnOfRelation(message: string): boolean {
188188
* path (`SqlDriver.execute`, which the native-SQL strategy runs on) since
189189
* #16019 — and `queryDataset`'s catch re-throws a declared envelope before
190190
* this question is asked (#5717 defence B). So a missing backing table raised
191-
* by `driver-sql` / `driver-turso` does NOT degrade on either strategy: it
192-
* answers `500 DATABASE_ERROR` at the door. What still degrades is an
193-
* undeclared producer — an embedder's own `executeRawSql`, a bare `Error`
194-
* from a driver outside this repo, the framework's not-registered signals.
191+
* by `driver-sql`, or by `driver-turso` embedded, does NOT degrade on either
192+
* strategy: it answers `500 DATABASE_ERROR` at the door. `driver-turso`'s
193+
* REMOTE transport is the exception and pre-dates this card: its typed exits
194+
* are undeclared, and `RemoteTransport.aggregate` swallows a missing table
195+
* into `[]` itself, so on the ObjectQL-aggregate strategy that deployment
196+
* answers `200` with no rows by the transport's own swallow. What still
197+
* degrades HERE is an undeclared producer — an embedder's own
198+
* `executeRawSql`, a bare `Error` from a driver outside this repo, the
199+
* framework's not-registered signals.
195200
*
196201
* ⚠️ It is a heuristic over driver PHRASING, so it is the SECOND question the
197202
* degradation path asks, never the first: {@link hasDeclaredErrorEnvelope} runs
@@ -1182,7 +1187,10 @@ export class AnalyticsService implements IAnalyticsService {
11821187
// #8931, the raw-SQL path the native-SQL strategy runs on since #16019 —
11831188
// and the `hasDeclaredErrorEnvelope` re-throw below (#5717 defence B)
11841189
// hands it to the door untouched, where it answers `500 DATABASE_ERROR`
1185-
// on both strategies. The degrade is not re-judged here; the ruling that
1190+
// on both strategies — for `driver-sql`, and for `driver-turso` embedded;
1191+
// the remote transport's typed exits are undeclared and pre-date this card
1192+
// (`RemoteTransport.aggregate` swallows a missing table into `[]` on its
1193+
// own). The degrade is not re-judged here; the ruling that
11861194
// drivers declare (and that a declared envelope is never re-read by its
11871195
// wording) decides it. Producers that still reach the degrade: an
11881196
// embedder's own `executeRawSql`, an out-of-repo driver throwing bare, the
@@ -1208,7 +1216,8 @@ export class AnalyticsService implements IAnalyticsService {
12081216
result = await new DatasetExecutor(this, orderLabels).execute(compiled, selection, context);
12091217
} catch (err) {
12101218
// The producer answered the classification question — the route's
1211-
// envelope reader serves it (4xx as itself, declared 5xx through the
1219+
// envelope reader serves it (4xx as itself; a declared 5xx relayed with
1220+
// the producer's own code by the door's ③a arm, #11718 — no longer the
12121221
// `ANALYTICS_QUERY_FAILED` path). Nothing here may re-judge it by wording.
12131222
if (hasDeclaredErrorEnvelope(err)) throw err;
12141223
if (isMissingSourceError(err)) {

0 commit comments

Comments
 (0)