Repository navigation
test(plugin-sharing): seam ObjectQL double holds the caller limit - #28670
Conversation
makeSeamQl().find filtered on `where` and dropped `limit`, so a test running through the seam saw more rows than the real engine would hand resolveAuthzContext. Slice the filtered rows by presence of a numeric limit, the shape check-objectql-double-limit prescribes, and pin it. Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 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
Coarse fallback — 11 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 c0027aad56b26a44a4e5e58e5cdd3ea4a3e92759 && git checkout c0027aad56b26a44a4e5e58e5cdd3ea4a3e92759
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3944d2e12a1ee67e6ab484959e5da80b2708dd73 e410091fd04f197eb28655062947520b397f7508 && git checkout -B drift-repro 3944d2e12a1ee67e6ab484959e5da80b2708dd73 && git merge --no-ff e410091fd04f197eb28655062947520b397f7508
node scripts/docs-audit/affected-docs.mjs --json 3944d2e12a1ee67e6ab484959e5da80b2708dd73 |
|
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC 席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, Fixes #28608. Two files (+49 / −3), labels size/s, tests, skip-changeset. Clause-②: no, Metadata-protocol: none. @objectstack/plugin-sharing ships files: ['dist', 'README.md', 'CHANGELOG.md'], and the testkit is not reached from the package entry, so skip-changeset holds. |
| Diff check (seat) | makeSeamQl().find keeps its where filter byte-for-byte and then applies limit by presence, after the filter (typeof opts?.limit === 'number' ? matched.slice(0, opts.limit) : matched). Slicing was chosen over throwing because resolveAuthzContext reads through the double with real bounds (1, 2, 200). makeSeamQl is now exported, and only the new pin exec-context-seam.limit.test.ts imports it: at most limit rows, the bound applied after the filter, and every match with no limit. Neither double gate script is touched (question 2 graded out). |
| Evidence | CI green on head e410091f (filter=latest: 35 success, 18 skipped, 0 failed; Test Core, TypeScript Type Check, Lint & Repo Gates and Build Core all success). Governed check: NOT governed, 52 changed lines. The report gives red first and an ablation that goes red and restores clean, @objectstack/plugin-sharing at 1299/1299, 51 of 52 derived gates exit 0 with 1 NOT MEASURED (check:dual-build-cjs-loads, exit 3, needs all dists), and the card's widened-walk probe (temporary, uncommitted) reading OK on head and BLIND at line 58 on base. The importer count is measured at 3 test files plus 1 type-only import, not five, all unchanged and green. |
| Landing | 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay, once every check on this head is green or an expected skip. |
Generated by Claude Code
Requested by Leehom · project thread
Fixes #28608
Clause-②: no
Metadata-protocol: none
维护者速读
makeSeamQl().find)现在会遵守调用方传入的limit,先按where过滤、再按limit截断。limit,resolveAuthzContext读这个替身时也确实传了limit(如选默认组织时的 1 和 2);替身丢掉它,测试看到的行比运行时多,可能在运行时不会出现的结果上变绿。dist不含这个 testkit,不发版;回滚即 revert 本 PR。packages/plugins/plugin-sharing/src/exec-context-seam.testkit.ts(find的 return 一行),packages/plugins/plugin-sharing/src/exec-context-seam.limit.test.ts。pnpm --filter @objectstack/plugin-sharing exec vitest run src/exec-context-seam.limit.test.ts,预期 3 passed。What changes
makeSeamQl().findinexec-context-seam.testkit.tsnow applies the caller's bound after thewherefilter, by presence:typeof opts?.limit === 'number' ? matched.slice(0, opts.limit) : matched. That is the first of the two shapesscripts/check-objectql-double-limit.mjsprescribes for a compliant double. It was picked over "throw on a bound it does not implement" becauseresolveAuthzContextreally does read through this double with bounds (limit: 1,limit: 2,limit: 200and others), so a throwing double would break every importer.wherehandling is unchanged.makeSeamQlis now exported, only so its own pin can drive it directly.exec-context-seam.limit.test.ts:limit: 1over two matching rows answers one row; the bound applies after the filter, not before it; nolimitanswers every match.Why
The double silently dropped
limit, so a test running through the seam could see more rows than the real engine would hand the resolver. Card #28608 question 1 (triage record 6557807357). Question 2 (widening the two double gates to walk*.testkit.ts) is graded out and not touched: neither gate script is edited.Tests
answers at most limit rows when more matchfails,expected [ Array(2) ] to deeply equal [ { id: 'a', …(1) } ], Tests 1 failed, 2 passed (3).scripts/ablation-replace.mjs: the limit line replaced byreturn matched;(anchor x1 to x0, blob d27c845e10d6 to 17d1ac7833f6), the same case reds (1 failed, 2 passed), then restored: blob after restore equals the HEAD blob d27c845e10d6 andgit diff HEADis empty. The testkit is source imported relatively by the test, so no dist leg applies.@objectstack/plugin-sharingsuite on head e410091: Test Files 59 passed (59), Tests 1299 passed (1299). The three test files that import the seam (sharing-service.test.ts,share-link-enforcement-context.test.ts,share-link-tenancy-posture-admission.test.ts, plus the type-only import inexec-context-annotation.pin.ts) pass unchanged.pnpm --filter @objectstack/plugin-sharing typecheck: exit 0 (test layer compiles, debt held).eslint --no-inline-config packages/plugins/plugin-sharing: exit 0.Acceptance notes
check-objectql-double-limit.mjscopied with itstestFilesUnderregex widened to(test|testkit).ts, never committed and deleted after. On head:OK ObjectQL double limit conformance holds: 643 double(s) graded, no plugin-sharing row. On the base testkit blob, same probe:exec-context-seam.testkit.ts ... (1 blind ...) line 58: BLIND, exit 1. Restore proven by blob equality and an emptygit diff HEAD.skip-changeset: plugin-sharing'sfiles[]isdist,README.md,CHANGELOG.md. After a build,makeSeamQlandbootRequestContexthave 0 hits there, while the positive controlSharingServicehas 4 files indist.Verification
Local subset on head e410091 (
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, 52 derived families), each exit captured before any pipe; reconciled with--ran:52 derived famil(ies) accounted for — 51 run, 1 NOT-MEASURED.check:objectql-double-limit,check:engine-double-contract,check:where-matcher,check:query-options-erasure,check:test-source-alias,check:type-check-debt,check:published-filesandcheck:nul-bytes.check-plugin-teardown-shape.mjs --self-test: exit 3 on the shallow clone first, exit 0 after fetching its pinned fixture commit.check:i18n: exit 3 (CLI not built) first, exit 0 after its one-command prerequisite build (all bundles in sync).check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET (needs every package dist in this fresh worktree); CI runs it.!in the title and no(narrowing)arm.Human Minimum
packages/specholds no runtime logic. (not touched)pnpm lintpasses (scoped to plugin-sharing).main, one pull request.skip-changeset)packages/spectouched: its generated artifacts are regenerated and committed. (not touched)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01FEkQ5kA7pasGpR4Pf7mtNb
Generated by Claude Code