Commit 5a95b0e
fix(types,metadata,cli): read the dialect text out of
* fix(types,metadata,cli): read the dialect text out of `cause` for operator records
Since #16019 the raw-SQL seam declares its own fault with a composed message
and keeps the dialect error under a non-enumerable `cause`, so every consumer
that embedded `error.message` into an operator-facing record began storing
"the database refused to run a raw statement" instead of "no such column: foo".
Add `operatorFacingErrorText` to `@objectstack/types` — a depth-bounded walk of
the `cause` chain, shaped like `matchesDriverError` — and apply it at the eleven
stored-record sites plus the one console site.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
* test: pin the operator-facing text in both directions at every site
Adds the helper's own unit cases (the three narrowings, the depth bound), the
site-level records in metadata-protocol and metadata, and the producer pin in
driver-sql that drives a real SqlDriver.execute() refusal through the helper so
a reworded envelope reddens there rather than in a customer's log.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
* chore: changeset for the operator-facing cause text
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
* test(driver-sql): hold the log sink from a subclass — `logger` is protected
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
* chore(changeset): grade the widened package `minor` and name driver-sql
`check-changeset-no-major.mjs`'s LEVEL AXIS refuses a clause-② `yes` PR that
grades NO package whose `packages/**/src/**` it moves at `minor` or above.
`@objectstack/types` is the package that actually grew — `operatorFacingErrorText`
is a new export — so it takes the `minor`; the rest stay `patch`, which is what a
bug fix in a released package takes. `@objectstack/driver-sql` joins the entry
list because this diff moves its `src/**` (one added test file); its published
`dist/` is byte-unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
* docs(types): correct the site count, the byte-for-byte claim and the pin pointer
Three text-only repairs from the clause-② contract review. No behaviour changes;
no package moves that were not already moving.
1. The changeset said "the eleven stored-record sites plus `os db clean`".
Re-measured on this tree: `operatorFacingErrorText(` occurs 15 times in
non-test source, one of which is the declaration, so 14 call sites — 13
stored-record sites plus the `os db clean` console line. The changeset's own
bullet list already summed to 13. Corrected to "thirteen". This file is
release-notes input, which is why the number matters.
2. "byte-for-byte for undeclared throws" was false, and it shipped: the docblock
it appears in belongs to the exported `operatorFacingErrorText` and reaches
`packages/types/dist/index.d.ts`. Two shapes are not byte-identical to what
the replaced expressions computed — a thrown non-`Error` now yields prose
where `(e as Error).message` yielded `undefined`, and an error with an EMPTY
message reads `Error` / `TypeError` through `|| String(error)` where those
expressions yielded `''`, or `unknown error` at the one site that ors in a
default. Both the docblock and the changeset's two copies of the claim now
say what the code does.
3. The `RAW_STATEMENT_FAULT_SENTENCE` docblock cited
`driver-error-classification.raw-statement-pin.test.ts`, which does not exist.
It now names the real producer pin,
`packages/drivers/driver-sql/src/sql-driver-16657-operator-facing-cause-text.test.ts`.
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
* docs(types): state the undeclared-channel RULE instead of enumerating its cases
The previous tidy round replaced a false absolute ("byte-for-byte for
undeclared throws") with a different false absolute: "Two shapes read
differently, and both read better", and "save for the two shapes named
above, where the text gets better rather than different in kind". A
contract-tier re-verification measured that false in four corners against
the built bundle. Re-measured here as a 10x5 matrix (9 shapes plus a
custom-named Error, against the helper and the four replaced expression
families) run against packages/types/dist/index.mjs; it reproduces the
re-verification's table exactly.
The wording now states the rule and marks its examples as illustrations:
an undeclared throw comes back as `messageChannelOf(error) || String(error)`
-- the value's own string `message`, the string itself for a thrown string,
`String(error)` otherwise -- with its `cause` never walked. Consequences,
not a closed list: an empty-message Error reads its `name` (a named
subclass reads the subclass name, not only Error / TypeError); a thrown
non-Error reads its own text or String(error) where `(e as Error).message`
read undefined, and where null / undefined threw a TypeError out of the
catch so no record was written and the operation aborted; an object
carrying a string `message` reads it where the instanceof-else-String
expression recorded [object Object]; a thrown EMPTY string reads '', which
is what makes both "never empty" and "yields prose" false.
Four carriers of the claim, not the two the re-verification located. A
content grep over the PR's 14 files found two more:
- messageChannelOf's own docblock still said "a thrown non-Error still
yields prose rather than `undefined`" -- the same false sentence, in
the same file, uncorrected by the previous round;
- driver-error-classification.operator-text.test.ts's file docblock still
carried the ORIGINAL claim verbatim, "an UNDECLARED throw is returned
byte-for-byte on its own message channel". The previous round corrected
two of that sentence's three copies.
No behaviour change: every changed line in both .ts files is a JSDoc ` *`
line, and each file's source with comment blocks stripped hashes identical
to HEAD. The `@returns` line's "never empty for a thrown value that has any
textual channel at all" was measured and left: it is conditional, and the
only value returning '' is a thrown empty string, whose channel is empty.
No claim is made about which shapes in-repo seams actually throw; that was
not measured.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
* docs(types,metadata,metadata-protocol,driver-sql): census by rule — every undeclared-throw claim measured
Fourth prose round on #16657. The census is by CLAIM, not by spelling: every
sentence in the PR's 14-file set that asserts what happens to an UNDECLARED
throw was enumerated and judged against the built bundle.
Blocking:
- driver-error-classification.ts: "so a record always carries a sentence rather
than `undefined` or an empty string" SHIPPED and was false. The fallback now
states only what it does — the same surface channel an undeclared throw reads,
which is '' exactly when that channel is. The suite carrying the same absolute
("a record is never empty or undefined") is renamed to what it pins.
- Both raw-exec-operator-detail-16657.test.ts docblocks said an undeclared throw
reaches the record "exactly as it did before". They now state what the pins
verify — not unwrapped, `cause` never walked, read on the value's own message
channel — and name the measured differences from the replaced expressions.
Non-blocking, same commit: the object-message illustration is scoped to a
NON-EMPTY `message` (3); the changeset's "one shape" count is corrected to the
three measured (4); "at every site this helper replaces" is scoped to the five
`(e as Error).message` sites of fourteen (5); the recognizer's "returned exactly
as it arrived" is scoped to a non-empty sentence, with the empty-message case
stated as measured (6); the eight identity-worded pin titles now name the
message channel instead of byte-identity (7).
Comments, JSDoc and test titles only: 82 changed lines across five .ts files,
0 not a comment or a title; comment+title-stripped sources hash identical to
HEAD in all five; 0 skips, `it()` counts unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
* docs(types): the empty-message fallback pin names the `name` it reads, not "prose"
The by-rule census caught one more carrier of the "always prose" half of the
claim, in a title no spelling-census would have matched: "answers prose for a
declared envelope whose own message is empty". Measured false in general — a
declared envelope whose `message` AND `name` are both empty answers '' — and it
contradicted the fallback sentence corrected in the previous commit. True of its
own pin, which reads `name` = 'Error'; the title now says that.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
* docs(types,metadata-protocol,changeset): state the split, and name the one surviving fallback
The census by rule found two claims carried in more than one place: a false
universal about what `(e as Error).message` did to an undeclared throw, and a
record formula that one of the fourteen call sites does not follow.
Over the five values pinned at the operator-text fallback case, that expression
did not answer one way. It read `undefined` for the string, the number and
`{}`, and it threw a `TypeError` for `null` and `undefined`. The pin's title and
the comment under it now state that split, which the shipped
`operatorFacingErrorText` docblock and both site docblocks already stated.
`seed-tenancy-backfill`'s organization probe keeps
`operatorFacingErrorText(e) || 'unknown error'`, so for an EMPTY channel it
records `'unknown error'`, not `''`. The metadata-protocol docblock, the pin
title that claimed every site, and the changeset -- which ships as release
notes -- now name that fallback and scope the formula to the other thirteen
sites. The fallback is load-bearing rather than leftover: the site reads an
empty value as "the probe did not fail", and with it removed an empty channel
routes the run down the benign no-organization-yet path. Whether it should go
is a behaviour question, tracked by #17167.
Two more sentences of the same class: the recognizer docblock now reads "a
NON-EMPTY sentence this fragment does not match" rather than "a DIFFERENT,
NON-EMPTY sentence", and `messageChannelOf`'s docblock no longer attributes
`undefined` to an expression that threw.
No behaviour change. 58 changed .ts lines, 54 comment and 4 title, 0 other;
comment-stripped and title-blanked hashes identical on all 13 .ts files; and
packages/types/dist/index.mjs, dist/index.js and dist/index.d.ts are each
byte-identical across this round.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
---------
Co-authored-by: Claude <noreply@anthropic.com>cause for operator-facing records (#17073)1 parent ea4d164 commit 5a95b0e
14 files changed
Lines changed: 1134 additions & 18 deletions
File tree
- .changeset
- packages
- cli/src/commands/db
- drivers/driver-sql/src
- metadata-protocol/src/migrations
- metadata/src/migrations
- types/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
112 | 113 | | |
113 | 114 | | |
114 | 115 | | |
115 | | - | |
| 116 | + | |
116 | 117 | | |
117 | 118 | | |
118 | 119 | | |
| |||
Lines changed: 91 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
Lines changed: 18 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
38 | 48 | | |
39 | 49 | | |
40 | | - | |
| 50 | + | |
41 | 51 | | |
42 | 52 | | |
43 | 53 | | |
| |||
356 | 366 | | |
357 | 367 | | |
358 | 368 | | |
359 | | - | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
360 | 373 | | |
361 | 374 | | |
362 | 375 | | |
363 | 376 | | |
364 | | - | |
| 377 | + | |
365 | 378 | | |
366 | 379 | | |
367 | 380 | | |
| |||
373 | 386 | | |
374 | 387 | | |
375 | 388 | | |
376 | | - | |
| 389 | + | |
377 | 390 | | |
378 | 391 | | |
379 | 392 | | |
| |||
0 commit comments