Skip to content

fix(objectql): the record-currency backfill cleans a driver's raw text out of a failed row, as the import door does - #30867

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-x3bsoc
Nov 6, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-x3bsoc

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

domain:server#1 · os-elon-musk · session_016WV4d1GEfMtjCJfYePyAPD

Fixes #30816

Clause-②: no
Metadata-protocol: none

Claim: comment 6678760289 (branch claude/project-thread-x3bsoc).

维护者速读

  • 改了什么:记录币种回填(POST /api/v1/meta/_migrate-record-currency-codes 与 os migrate record-currency-codes)报告里失败行的错误文字,非沙箱钩子的报错现在先经导入入口同一个清洗函数 sanitizeRowError 再返回。
  • 为什么改:回填报告自 feat(rest, client): a tenant-admin REST door for the record-currency code backfill #30809 起经 REST 返回给持 setup.write 的租户管理员,数据库驱动的原始报错(SQL 语句、约束名、列名)会原样外露;导入入口早已清洗同类报错。
  • 风险与回滚:只改一行取值,沙箱钩子分支不动;不含驱动文字的报错照旧原样;运维日志行仍保留完整原文。单一提交可整体回滚。
  • 先看:packages/objectql/src/record-currency-backfill.ts(rowFailureSentence 的最后一行)、packages/rest/src/meta-record-currency-backfill-route.test.ts(REST 钉)。
  • 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run src/record-currency-backfill.test.ts,预期 21/21 通过。
  • 需要你做的:无,席位落地。

What changes

  • rowFailureSentence (@objectstack/objectql): a row whose write threw anything other than a sandboxed hook's error now reads sanitizeRowError(message), the import row's rule from @objectstack/core, instead of the raw message. The sandboxed arm (the door's withheld sentence for a crash, the hook's own sentence for a refusal) is unchanged. The operator's logger.warn line still carries the whole text.
  • No new export and no new dependency edge: sanitizeRowError is already public (packages/core/src/index.ts re-exports ./utils/import-runner.js with export *; @objectstack/rest tests already import it from @objectstack/core), and @objectstack/objectql already depends on @objectstack/core. So the card's premise that core does not export it does not hold, and Clause-② re-derives to no instead of the claim's expected yes (widening). ⛔ No second copy of the sanitizer's rules.
  • The CLI report changes in the same way, because it prints the same failures[].error through formatRecordCurrencyBackfillReport. The operator loses nothing: the CLI routes logger.warn to its warning line (stderr under --json), which keeps the raw driver text.

Why

Since #30809 the backfill report is also the REST answer of POST /meta/_migrate-record-currency-codes, read by a tenant admin holding setup.write. A driver's error kept its SQL statement, constraint name and column list verbatim in that answer, while the import door cleans the same class. Contract review 6677255440, should-fix 1.

Tests

  • packages/objectql/src/record-currency-backfill.test.ts: three driver-shaped throws (an update ... - REASON statement, a SQLite UNIQUE failure, an insert ... - FOREIGN_KEY_REASON statement) each read sanitizeRowError(raw) in the report and none of the driver's names; the printed report line reads the same sentence; the warning line keeps the raw text. The existing CONTROL (a plain message with no driver text) still reads as before.
  • packages/rest/src/meta-record-currency-backfill-route.test.ts: on the real RestServer route over a real ObjectQL engine, the driver throws a driver-shaped error for one row of the caller's organization; the answer is 200, the other row is written, and the failed row carries sanitizeRowError(raw) with no SQL, column or placeholder text.
  • Found while writing the REST pin: a unique violation never reaches rowFailureSentence raw, because the engine already rewrites it into its own duplicate-record sentence. The REST pin therefore uses a value-too-long driver error.
Ablation (restored, blob equal to HEAD, git diff HEAD empty)
  • Mutation ?? sanitizeRowError(message); to ?? (message || 'ABLATED_30816'); via scripts/ablation-replace.mjs, objectql rebuilt, ablation-dist-preflight found the marker in 2 built files (the REST suite reads objectql's dist/).
  • objectql: 1 failed / 20 passed (the driver-shaped case). REST: 1 failed / 12 passed (the new pin).
  • Restore leg: blob 5fd2c441 equals HEAD, git diff HEAD empty, objectql rebuilt, preflight --absent green, both files green again (21/21, 13/13).
  • The mutate leg's objectql build exited 1 after emitting JS (the marker reached dist); the restore build exited 0.

Acceptance notes

  • sanitizeRowError keeps the trailing reason of a ... - REASON driver message when the reason is not itself SQL, so a reason such as value too long for type character varying(3) is still shown. That is the import door's existing reading, applied here unchanged.

Verification

All on head 9e4afd8b:

  • pnpm --filter @objectstack/objectql typecheck and test: 558 files, 11160 tests passed.
  • pnpm --filter @objectstack/rest typecheck and test: 393 files, 7456 passed, 332 skipped.
  • node scripts/pm/dispatch-gates.mjs --commands: 61 families derived; --ran reconciliation: 60 run with exit 0, 1 NOT MEASURED (pnpm check:dual-build-cjs-loads, exit 3, PREREQUISITE NOT MET: the whole tree has no dist/; CI runs it on a full build).
  • pnpm lint: exit 0.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: not touched.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…t out of a failed row

A non-sandboxed row failure now reads sanitizeRowError's sentence, the
import door's rule, in the report the REST door returns; the operator's
log line keeps the whole text.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4d1GEfMtjCJfYePyAPD
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 63 of 222 client-bound route-ledger rows — the other 159 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 159: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 104 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 21 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 042efb7f12e935c913d1d6e323d2101827c78998 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 05b46a2e773d5f9e8e9074a95c60971c86e46b1e — the merge of head 9e4afd8b1ac350539c472596a057ca754bfeeb91 into base 042efb7f12e935c913d1d6e323d2101827c78998, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 05b46a2e773d5f9e8e9074a95c60971c86e46b1e && git checkout 05b46a2e773d5f9e8e9074a95c60971c86e46b1e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 042efb7f12e935c913d1d6e323d2101827c78998 9e4afd8b1ac350539c472596a057ca754bfeeb91 && git checkout -B drift-repro 042efb7f12e935c913d1d6e323d2101827c78998 && git merge --no-ff 9e4afd8b1ac350539c472596a057ca754bfeeb91

node scripts/docs-audit/affected-docs.mjs --json 042efb7f12e935c913d1d6e323d2101827c78998

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

席位核对 · ACCEPT · 2026-11-06T07:48Z · #30816

Head-sha: 9e4afd8

  • Shape: 4 files, +61/−5. The changes:

    • rowFailureSentence in packages/objectql/src/record-currency-backfill.ts: one call and the docblock.
    • One objectql test and one REST route test.
    • One @objectstack/objectql patch changeset.

    It sits inside claim 6678760289 and is not governed.

  • Premise correction (dev, verified by the seat): sanitizeRowError was already public, because packages/core/src/index.ts :104 does export * from './utils/import-runner.js'. So core gains no export, the card's "would widen core" note does not apply, and there is still exactly one copy of the sanitizer's rules.

  • Spot check (read in the diff):

    • The sandboxed arm is unchanged. Only the fallthrough changes, from message to sanitizeRowError(message).
    • sandboxBusinessMessage still wins for a sandboxed refusal.
    • The operator's logger.warn line keeps the raw text, and the test asserts it.
    • The objectql test covers three driver shapes: a Postgres value-too-long with SQL, a SQLite UNIQUE, and a Postgres FK. Each row reads the cleaned sentence, matches none of the table, placeholder, column or constraint names, and keeps the raw text in the warning.
    • The REST test shows a tenant admin gets the cleaned sentence for the failed row while the other row is still written.
    • The no-driver-text control is kept.
  • CLI: the printed report changes the same way, because it is the same failures[].error. The raw text stays on the CLI's warning line (stderr under --json), so the operator loses nothing. The body says this.

  • Gates: every check-run on this head has concluded: 48 distinct checks, the latest run of each 35 success, 13 skipped, 0 failure.

    • Locally (dev report 6679513589), 60 of 61 derived families exit 0 and pnpm lint exits 0.
    • check:dual-build-cjs-loads was NOT MEASURED locally (exit 3, no whole-tree dist). CI's build lanes on this head cover it.
    • The ablation turns both pins red (objectql and REST).
  • Contract: Clause-②: no (re-derived: no export, no shape change; only a failure sentence is cleaned) and Metadata-protocol: none.

  • Landing: ready, then auto-merge, through the relay. Fixes #30816.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 6, 2026 07:50
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 6, 2026 07:50
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 555eb1b Nov 6, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-x3bsoc branch November 6, 2026 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

record-currency backfill over REST: a non-sandboxed row failure answers raw driver text to the tenant admin; the import door sanitizes the same class

2 participants