Repository navigation
fix(objectql): a data driver receives the declared driver options and nothing else - #30151
Conversation
… nothing else The engine now hands every driver call the keys DriverOptionsSchema declares (read off the schema, minus retired tombstones) and nothing else. buildDriverOptions returns that declared set; find, findOne, insert, update, delete and the raw execute door hand it to the driver, while input.options stays the hooks' additive view of the caller's bag. Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj Co-authored-by: Claude <noreply@anthropic.com>
…n keys A recording driver double reads the options argument as the driver receives it on find, findOne, insert, update, delete (by id and multi), count and the raw execute door: every key is one DriverOptionsSchema declares. Controls pin that transaction, tenantId, timezone, preserveAudit and an explicit bypassTenantAudit still arrive, and that the after* hook view of input.options stays additive. Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj Co-authored-by: Claude <noreply@anthropic.com>
…er options only Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj Co-authored-by: Claude <noreply@anthropic.com>
check:objectql-double-limit grades every in-memory find double: apply the caller's bound by presence after the filter, or refuse it. Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj Co-authored-by: Claude <noreply@anthropic.com>
…dleware reads it Four engine.test.ts cases asserted the expansion marker on the driver's options argument. The driver is now handed declared driver options only, so those cases assert that, and the file-reference case observes the marker at the middleware seam, as the existing lookup-expansion routing case does. Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj Co-authored-by: Claude <noreply@anthropic.com>
…'s other declared keys Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 172 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 dd8d678dbca047260419b947712700c3e71c44b7 && git checkout dd8d678dbca047260419b947712700c3e71c44b7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 471add32df3f89315156cf982586b40e193bb84f 6aaa0350bb42d2e4de85c8132785f971a24b32e6 && git checkout -B drift-repro 471add32df3f89315156cf982586b40e193bb84f && git merge --no-ff 6aaa0350bb42d2e4de85c8132785f971a24b32e6
node scripts/docs-audit/affected-docs.mjs --json 471add32df3f89315156cf982586b40e193bb84f
|
|
security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj Contract-review-claim: session_01QSeKYn5CmmdShCJifm6WYj · head 10142d7 · 2026-11-04T16:39Z |
|
security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj Contract reviewServed-tier: Ruling applied: the engine hands a data driver the declared ① Derived judgments
② Semver level
AGENTS.md Post-Task Checklist step 3 is the text: " The dev's question 2 — does dropping keys a third-party driver could read only with a cast count as narrowing — no. The published surface at the hop is the ③ Boundary flags
④ Design judgment
No must-fix. Four should-fix items (the Implemented-by: VERDICT: PASS |
…er options rule - the no-bag hook view is a copy, never the driver's bag itself - the hook PHASE paragraph names buildDriverOptions (the driver's set) and mergeDriverOptionsIntoHookView (the merge into the hook view) - the pass-through keys docblock says the bag's declared keys are the base - the four re-pointed engine.test.ts assertions hold the received keys to a subset of declaredDriverOptionKeys() - the changeset says undeclared keys, and qualifies which doors admit the schema's other declared keys Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj Co-authored-by: Claude <noreply@anthropic.com>
|
security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj Contract reviewServed-tier: Delta review by the same reviewer of the one commit ① Derived judgmentsEach should-fix item from the earlier record, against the diff:
No new driver call site, no change to any hop, no change to the declared-key reader or the pick. The file list grows by
② Semver levelUnchanged and still consistent: ③ Boundary flags
④ Design judgment
No must-fix. No new should-fix. Implemented-by: VERDICT: PASS |
|
security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj 席位核对ACCEPT · head
|
Clause-②: no
Metadata-protocol: none
维护者速读
DriverOptionsSchema声明的键(从 schema 读出,去掉退役墓碑timeout)。buildDriverOptions返回的就是这组键;find、findOne、count、aggregate、insert、update、delete(按 id 与多行)以及原始execute都把它交给驱动。hook 读到的input.options不变,仍是调用方的选项袋加上驱动选项(加法视图)。transaction、tenantId、tenantIds、timezone、preserveAudit、bypassTenantAudit)原样到达,有对照测试;仓内驱动都只读声明键。若有第三方驱动读未声明的键,它将读不到(仓内没有)。@objectstack/objectqlpatch,spec 只改两处注释。回滚 = revert 本 PR。packages/objectql/src/engine-driver-options.ts的pickDeclaredDriverOptions、mergeDriverOptionsIntoHookView和buildDriverOptions早返回那几行;②engine.ts里 find / findOne / insert / update / delete 把driverOptions交给驱动、把合并视图留给 hook 的位置。pnpm --filter @objectstack/objectql exec vitest run --project local src/engine-driver-options.test.ts,预期8 passed;把buildDriverOptions里的pickDeclaredDriverOptions(base)换回{ ...base }再跑,第二个用例转红。What changes
The engine → driver hop is now a closed contract, one hop below the engine option bags #4400 closed: a data driver receives the keys
DriverOptionsSchemadeclares and nothing else.packages/objectql/src/engine-driver-options.tsdeclaredDriverOptionKeys()reads the key set offDriverOptionsSchema.shapeon first use, minus any member that admits only absence (aretiredKey()tombstone, todaytimeout). It is never restated, so a key the spec declares reaches drivers with no edit here.pickDeclaredDriverOptions(bag)cuts a bag to that set (own enumerable keys, values untouched).buildDriverOptionsstarts from the declared keys ofbaseinstead of a copy of the whole bag, so every bag it returns is declared-only. A caller's explicittenantId/transaction/bypassTenantAuditstill wins, exactly as before.mergeDriverOptionsIntoHookView(callerBag, driverOptions)builds theafter*view ofinput.options: the caller's bag with the driver options merged on, additive as the hook PHASE contract states; the caller's own object when nothing is added; and a copy of the driver options when there is no caller bag, so the hooks' view and the driver's bag are never one object.packages/objectql/src/engine.ts:find,findOne,insert,updateanddeletekeep the built options in a localdriverOptions, hand THAT to every driver call (including the reads and writes the verb makes along the way), and assign the merged view toinput.optionsfor hooks.packages/objectql/src/engine-execute-transactions.ts: the rawexecutedoor hands its driver the declared keys of its bag.packages/objectql/src/engine-top-level.ts: comment-only. The pass-through keys docblock now says the bag's declared keys are the base of the driver options.packages/spec/src/data/hook.zod.ts,packages/spec/src/data/driver.zod.ts: comment-only. The hook PHASE paragraph said theafter*handler and the driver read one view; it now says the merged view is the hooks' and a driver is handed the declared keys, and it names bothbuildDriverOptions(which produces the driver's set) andmergeDriverOptionsIntoHookView(which merges that set into the hook view).DriverOptionsSchema's doc states the hop rule..changeset/driver-options-declared-keys-only.md:@objectstack/objectqlpatch.Tests
packages/objectql/src/engine-driver-options.test.ts(new, 8 cases), on a recording driver double that captures the options argument as the driver receives it:a data driver receives the declared driver options and nothing elseDriverOptionsSchema's declared keys, and every shape key it leaves out refuses every probe value (a tombstone);find,findOne,insert,updateanddelete(by id and multi-row;count,updateMany,deleteManyasserted reached too) hand the driver only declared option keys;executedoor hands the driver only declared option keys.control: the declared keys the engine threads still reach the driver:transaction,tenantIdandtimezonearrive on every verb;preserveAuditand a caller's explicitbypassTenantAuditarrive unchanged.the after* hook view of input.options stays additive: anafterFindhook still reads the caller's own keys beside the driver options; anafterUpdatehook still reads a key the caller passed; the merge helper answers the caller's own bag when it adds nothing, and a copy, never the driver's bag itself, when there is no caller bag.packages/objectql/src/engine.test.ts: four expansion sub-read cases hold the driver's received option keys to a subset ofdeclaredDriverOptionKeys(); the file-reference case reads the expansion marker at the middleware seam.Acceptance notes
Clause-②: no, no arm,Metadata-protocol: none,patch. No accept set moves: every engine door accepts the option bags it accepted, andDriverOptionsSchemais unchanged apart from its doc comment. The keys that stop reaching a driver were never declared on the driver contract (theDriverOptionstype carries none of them, so a typed driver could not read one without a cast), and no driver in this repository reads one. What a metadata author can write does not change.buildDriverOptions's result used to be both the driver's options and theafter*hook view ofinput.options. Hooks in this repository read caller keys from that view after the merge (anafterInsert/afterUpdatehandler reads one there), and the hook PHASE contract states the merge is additive. So the declared set is what the driver is handed, and hooks keep the additive view; the third test block pins that.after*per-row dispatches read the same merged view as before.insertand the rawexecutedoor hand them on when a caller sets them;find,findOne,updateanddeleteadmit only the six threaded keys at their own door (ENGINE_DRIVER_PASSTHROUGH_KEYS), unchanged by this PR.Verification
Head: 6aaa035 (the contract-review should-fix round). Every reading below is against that tree.
Tests, typecheck, full suite, and the one-shot ablation of the no-bag copy
Derived gate families