Repository navigation
fix(driver-sql): the five refusal log lines take the shared driver-fault cut, moved to core - #21414
Conversation
…ult cut, moved to core The driver-fault redaction module moves from objectql to core so driver-sql can import it. The read terminal, the raw terminal and the three unresolvable-column refusals now write the dialect text through the log face of that cut with statementSent, and the raw terminal no longer writes the sent statement. Code, class of fault and the dialect diagnostic are kept. Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
…lip the old statement-in-log pins Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
…iver-log-redaction
…from core Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
…e shared driver-fault cut Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
…iver-log-redaction
…and a control case Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 21 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 5 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 26 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a6867ed274658daedd7c4f2c210db669f58a0ff6 && git checkout a6867ed274658daedd7c4f2c210db669f58a0ff6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc 992e940fff9b056e18b26d91363b026ca544c2fd && git checkout -B drift-repro ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc && git merge --no-ff 992e940fff9b056e18b26d91363b026ca544c2fd
node scripts/docs-audit/affected-docs.mjs --json ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc
|
|
CI note on head
Generated by Claude Code |
…iver-log-redaction
…e lowest home every face reaches; core leaves the diff Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
…name @objectstack/types Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
Fixes #21385
Clause-②: no
Security handling: this body, the commits, the changesets and the test names carry classes and positions only. Every pin plants a synthetic sentinel and asserts it is ABSENT. No measured log line is copied anywhere, and the local probe printed booleans only.
What this changes
This implements the maintainer's ruling on #21385 (comment 5950942037, letter A): one cutter for every log face. The cutter's home is
@objectstack/types, per the claim's amendment 1 (5954587444) and triage's pointer (5954318919). See "Patch round 1" below.The redaction module moves to
@objectstack/types.packages/objectql/src/driver-fault-redaction.tsbecomespackages/types/src/driver-fault-redaction.ts. Againstmain, that is the PR's one git rename (R092). The cut is the same code: the same split, the same structural cut at the separator, the same value templates and the same property rules.looksLikeInternalErrorLeak, is now the package-local./error-leak.js.error-leak.tsand its frozen list are not edited.redactBoundStatement,redactStatementFromMessage,redactPropagatedDriverFaultand theDriverFaultOrigintype.packages/types/src/driver-fault-redaction.test.ts.types: it is the lowest package every face of this family can reach.driver-sql,objectqlandcoreall depend on it, and the family's fourth position,operatorFacingErrorText, lives inside it ([security] operatorFacingErrorText hands a raw statement fault's cause message, statement and inlined values included, to its operator-facing callers: the fourth position of the #21274 / #21345 / #21385 family #21418). The module header says so.error-leak.tsordriver-error-classification.ts, and no row added to the frozen predicate list.packages/coreis not in the diff.One widening, on the log face.
redactStatementFromMessagetakes an optional second argument: the same{ statementSent: true }thatredactPropagatedDriverFaultalready took. Without it, the answer is unchanged.objectql calls the moved code.
engine.tsandlifecycle/lifecycle-service.tsnow import from@objectstack/types, with the same arguments as before. Five objectql test files repoint their import. None of the moved names was on objectql's entries.driver-sql's five lines call it. In
sql-driver.ts, each refusal line writes the dialect's text throughredactStatementFromMessage(text, { statementSent: true }), imported from@objectstack/types. The lines are:INVALID_FILTER), onfindand oncount;DATABASE_ERROR);DATABASE_ERROR), which no longer writes the sent statement as a separate field;INVALID_FIELD);INVALID_FIELD).statementSentholds by construction: each line writes a fault raised by a statement this driver sent, which is the same knowledge the engine's raw door passes. The read terminal's cut sits where its text is computed, so the two debug lines that demote it inside a scope take the cut too (see Acceptance notes).driver-turso's remote transport is covered by the same line. Its refusals reach the base class's raw terminal. Its transport error carries no statement (the bare shape), so the line now writes the engine's diagnostic and nothing of the statement it was sent.Two envelope sentences are corrected. The read terminal's and the raw terminal's composed
DATABASE_ERRORmessages said the statement was written to the server log. After this change that is not true, so each now says the diagnostic was written, with the statement and its bound values cut. Code, status,causeand the withheld text are unchanged. See Acceptance notes, deviation 1.Measured: sentinel on each line, before and after
Probe: drive each line through the public driver method on better-sqlite3, a live PostgreSQL 16 and a live MySQL 8.0.46, with one synthetic sentinel. An extractor printed only whether the captured line held the sentinel, and on which field. BEFORE was measured at
ecb6ca0258(base), and AFTER at0cbd86d55e, with the same extractor. The committed pins re-measure AFTER at the final head,992e940fff.findcountAfter the change, on every row: the lead (code and class of fault, object and column) is kept, the dialect's own diagnostic is kept, and the cut's marker stands where the statement was. On the pg read and raw rows, the 22P02 value slot reads as the value marker. The raw line no longer carries a
statement:field.The
driver-tursoremote transport, with a sentinel spelled inline in the sent statement: the raw terminal was handed it in the statement and not in the transport's bare error, and the line carries none of it. No marker appears there, because the bare error has no statement to cut.Zone 2 hypotheses
H1 confirmed, with one amendment. All five lines were driven on all three dialects. At base they sit at
sql-driver.ts:10087,:10215,:10284,:10960and:11253. The amendment: on PostgreSQL, the three unresolvable-column lines never carried the sentinel. pg positions bindings as$nbefore knex formats the message, and its column diagnostic names identifiers only. Those three pg cells are recorded in the pin as non-regression cells, not as non-vacuous ones. The pg read and raw terminals did carry it, through the 22P02 diagnostic and through the raw statement's own text.H2 superseded in patch round 1. Round 0 confirmed that
packages/corecould hold the module. Amendment 1 moved it topackages/types, where it also fits. Measured, the package takes a new runtime module:tsupentry issrc/index.ts(edge-safe; the module uses nonode:builtin);tsconfigincludessrc/**/*, and--listFilesshows both the module and its test;check-dts-emittedruns in its build, andcheck:dual-build-cjs-loads,check:lean-entry-closureandcheck:published-filesall pass;The WHOLE module moved, not only the cutter. The two faces and the property rules are thin wrappers over the module's private split, so objectql no longer has a wrapper of its own.
H3 confirmed. Each line keeps its lead, plus
(CODE)on the read and raw lines, and the dialect's diagnostic. It loses the statement and any template-owned value. The raw terminal's separately loggedstatement:field carried an inline sentinel on all three dialects at base, and it is no longer written.H4 partly falsified. Every envelope's code, status,
causeand withheld text are unchanged. But two composed messages described the log half ("the statement … written to the server log"), and this change made them false. They were corrected; see deviation 1. The engine's existing pins for the two earlier positions (the cards behind PR fix(objectql): redact a driver fault where it leaves the engine, not only in the engine's log line #21335 and PR fix(objectql): the raw-statement door and the lifecycle sweep cut a driver fault by construction (#21345) #21384) stay green:driver-fault-boundary-redaction.test.tsanddriver-fault-redaction-residue.test.ts;raw-statement-fault-redaction.test.ts;Pins
New:
packages/drivers/driver-sql/src/sql-driver-21385-refusal-log-line-redaction.test.ts, 46 cases. It runs on every cell of the driver axis: SQLite always, live PostgreSQL and MySQL when their URLs are set.Flipped: nine existing pins in eight files. Each of these pinned the retired design, in which the statement was in the log line:
driver-sql:sql-driver-11455-…,-11541-…,-16019-…,-17639-…,-17857-…,sql-driver-backend-fault-envelope(two cases) andsql-driver-unresolvable-where-column-refusal(the positive control);driver-turso(patch round 1):turso-driver-16019-remote-raw-statement-fault-envelope.Each now asserts the diagnostic and that the statement is absent. The
driver-sqlones also assert the marker. The positive controls, and the turso pin, read the sentinel's presence on what the refusal was handed (or on the envelope'scause) instead of on the log.Reverse verification
The five calls (round 0). The change was committed first (head
7445ed5cda,sql-driver.tsblobede93583a0f1). Only the five calls were reverted: the four identical call sites and the read terminal's one. This used two nestedscripts/ablation-replace.mjslegs: anchor x4 → x0 (blobede93583a0f1→e4243feaf00e), then anchor x1 → x0 (blobe4243feaf00e→a8ea87a6014b). The driver-sql pin loads./sql-driver.jsfrom source, so no build sits between the mutation and the run.git diff HEADis empty.The turso flip (patch round 1, at
992e940fff). Only thedriver-sqlchange this pin depends on was reverted: the raw terminal's droppedstatement:field was put back.driver-tursoreadsdriver-sqlfrom its dist, so each leg was rebuilt and its dist checked before the run.ablation-replace.mjsrefused it (anchor x1 → x1) and ran nothing. The anchor was changed and the leg re-run.ae07547bcd68→025ec6208050. The on-disk marker count was 1.driver-sqlwas rebuilt, andablation-dist-preflight.mjsfound the marker present in 2 built files.16019raw-line pin.ae07547bcd68, andgit diff HEADis empty. After a rebuild, the preflight with--absentfound the marker absent from all 6 built files. The turso pin went back to 4/4, and the driver-sql raw pins to 53/53.Verification
Final head:
992e940fff. Live servers: PostgreSQL 16 and MySQL 8.0.46, throwaway instances that were stopped by recorded PID and removed.TZ=America/New_Yorkwas set, as the live CI job sets it.@objectstack/types:test23 files / 696 tests passed;test:repo1 file / 7 tests passed;typecheckexit 0.@objectstack/core(the module left it):test76 files / 2156 tests passed;typecheckexit 0.@objectstack/objectql:test365 files / 7379 tests passed;test:repo1 file / 5 tests passed;typecheckexit 0, withcheck:test-typecheckOK.@objectstack/driver-sql, full suite (vitest run --maxWorkers=2), with both live URLs: 227 files passed; 5472 tests passed and 1 skipped; 0 sentinel occurrences in the run log.typecheck: exit 0.@objectstack/driver-turso:test88 files passed; 2365 tests passed and 33 skipped.typecheck: exit 0.@objectstack/driver-sqlite-wasm:test36 files / 675 tests passed;typecheckexit 0.The
Test Core (1/6)packages that CI never reached:spec:test600 files, 17606 passed + 1 todo;test:repo48 files / 849 passed;service-settings33 / 591;cloud-connection31 / 401;service-messaging46 / 507;example-showcase31 / 394;plugin-pinyin-search2 / 21;connector-mcp3 / 23;knowledge-memory1 / 8.All exited 0.
Dogfood, against freshly built dists (
turbo run build --filter=@objectstack/dogfood... …, 63/63), with both live URLs:test171 files passed and 1 skipped; 1402 tests passed and 3 skipped.typecheckexit 0, afterpnpm install --frozen-lockfile. The first attempt failed on a missing link for@objectstack/trigger-api: themainmerge had added that dependency, and this worktree's install predated the merge.The plugin-auth carrier pin (
driver-fault-auth-log-carriers.test.ts), on three dialects: 15/15.The runtime files that read these lines (
seed-tenancy-autonumber-split,expected-read-refusal-noise.channel-asymmetry,metadata-list-ambient-vs-bare-transaction,first-boot-migration-gate-read): 4 files, 22 tests passed.Gates:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 70 at992e940fff. All 70 ran with exit 0, and--ranreconciles 70 derived / 70 run / 0 NOT-MEASURED / 0 UNRUN.check:dual-build-cjs-loadsfirst answered PREREQUISITE NOT MET (exit 3) for 8 packages without a dist. After those were built, it exited 0.pnpm check:live-db-isolationPASS, and a control-byte self-scan of the 26 touched files, with no hit.ESLint, narrowed to the 23 touched TS files, at
992e940fff:eslint.config.mjsispackages/**/*.{ts,tsx,mts,cts};--format jsongives 23 files, 0 errors and 0 warnings;parserOptions.project), so this diff cannot move an untouched file's verdict.pnpm lintis CI's.Patch round 1
7445ed5cdawent red inTest Core (1/6), on thedriver-tursopin that asserted the statement on the raw terminal's line. That pin is this PR's, and it is flipped here. The shard's first attempt had failed earlier, in an unrelatedspechook timeout (Flaky on Test Core (1/6): the tombstone describe of packages/spec check-liveness.test.ts times out in its beforeAll at 10000ms — the hook spawns the whole liveness gate under vitest's default hookTimeout, which the spec config cannot raise #21421). Separately, the seat moved the cutter's home to@objectstack/types(amendment 1, 5954587444), so that [security] operatorFacingErrorText hands a raw statement fault's cause message, statement and inlined values included, to its operator-facing callers: the fourth position of the #21274 / #21345 / #21385 family #21418 can call it fromoperatorFacingErrorText.packages/coretopackages/types, and core's export block was removed. Every importer was repointed, with the module-path strings and comments that named core. The coreminorchangeset became a typesminorone, and the driver-sql and objectql changesets now name@objectstack/types. Net againstmain: one rename,objectql→types;git diff origin/main...HEAD -- packages/coreis empty.git grep -n -Eover the test files of all 18 packages that depend on@objectstack/driver-sql. The pattern covered the five lines' leads, the[sql-driver]prefix,refused a raw statement/refused a read on/could not be resolved on,kept server-side,statement:, and anincludes('DATABASE_ERROR' | 'INVALID_FILTER' | 'INVALID_FIELD')filter.driver-tursopin above.dispatch-gates --commandsderive the suites of packages that depend on a changed package? Measured: no.check:*gate families. Of its 70 commands at992e940fff, none is a package test suite: the only--filtercommand isspec'scheck:duration-unit-keys.driver-turso.Acceptance notes
causeare unchanged.operatorFacingErrorText's copy matches only the leading "refused to run a raw statement" fragment, which is kept; the producer pinsql-driver-16657-…is green.metadata,metadata-protocol,rest,typesandobjectql. They stay green because the matcher keys only on the leading fragment. They are left as they are: four of the files sit outside this claim's surface, and editing only the fifth would split the copies.statement:field. Non-vacuity is read off the sent statement the raw terminal was handed.TZ=America/New_York, as CI's live job does.Out of scope (reported, not edited)
operatorFacingErrorText(@objectstack/types) hands the raw path'scausetext back whole. Measured on SQLite with a synthetic sentinel bound into a raw statement: the envelope's message carries none, and the helper's answer carries it. This is [security] operatorFacingErrorText hands a raw statement fault's cause message, statement and inlined values included, to its operator-facing callers: the fourth position of the #21274 / #21345 / #21385 family #21418's (the family's fourth position), which can now call the cutter from the same package. Its docblock also says the driver writes the statement one line earlier, which is no longer true.packages/qa/dogfood/test/raw-statement-fault-redaction.test.ts(its header says the driver's raw line writes the statement);sql-driver-diagnostic-value-probe.test.ts's rationale for not importing the redactor (the module paths in its failure messages are updated here).Generated by Claude Code