Skip to content

test(plugin-auth): migrate the contains witness to sqlite, now that $regex is refused (#5893) - #6665

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5893-auth-contains-sqlite-witness
Aug 8, 2026
Merged

test(plugin-auth): migrate the contains witness to sqlite, now that $regex is refused (#5893)#6665
os-zhuang merged 1 commit into
mainfrom
claude/issue-5893-auth-contains-sqlite-witness

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5893

Discharges the deferral #5830 recorded as ruling C: auth-contains-filter.test.ts's witness backend moves from driver-memory to sqlite, on the expiry condition that card wrote for itself rather than on a second opinion.

Premise verification (before implementing)

Verified against origin/main @ e6025e9, not against the issue text:

  • drivers: $regex 响亮拒收 + $icontains 各后端实现(#4706 裁决 B 案 · 驱动半边) #5702 landed. driver-sql's emitter refuses $regex by name — sql-driver.ts's default: arm calls retiredFilterOperatorError before the generic unsupported-operator message, and the family's own suites assert code: 'INVALID_FILTER' + status: 400 on it (sql-driver-null-operators.test.ts, sql-driver-icontains-and-retired-operators.test.ts).

  • The PM's mechanism assumption holds, and it was measured rather than assumed — the dispatch explicitly invited falsification. Driving the PR test(plugin-auth): where-operator 覆盖测试迁到 sqlite :memory: 见证;contains pin 实测不可迁,留证上报 (#5830) #5880 harness path (SqlDriver + better-sqlite3 :memory:) directly:

    CONTAINS "a.b" -> ["a.b"]        REGEX "a.b" -> code=INVALID_FILTER status=400
    CONTAINS "^a"  -> []             REGEX "^a"  -> code=INVALID_FILTER status=400
    CONTAINS "("   -> ["x(y"]        REGEX "("   -> code=INVALID_FILTER status=400
    CONTAINS "xb"  -> ["axb"]        REGEX "xb"  -> code=INVALID_FILTER status=400
    

    The four $contains answers are value-for-value what the memory backend answered, so the migration costs the fixture nothing; the refusal is reachable through the harness, so the defect is witnessable again.

  • Consumer census. On origin/main, plugin-auth's only real driver-memory consumers were this test file's import and the package.json devDep. The other grep hits are prose comments and stay — retirement verification greps declarations, not mentions.

What changed

  1. Harness migrationSqlDriver + better-sqlite3 :memory: + real DDL through initObjects, copied from the sibling auth-where-operator-coverage.test.ts (PR test(plugin-auth): where-operator 覆盖测试迁到 sqlite :memory: 见证;contains pin 实测不可迁,留证上报 (#5830) #5880). Seed declares only the two columns assertions actually read; the three camelCase keys the old fixture carried (emailVerified / createdAt / updatedAt) are dropped rather than declared, since nothing looked at them and only a schemaless store accepted them (test(rest,plugin-approvals,trigger-record-change): 7 个 integration 测试真迁 driver-sql sqlite :memory:(#5704 批次 3) #5806's "resolve by declaring, not by relaxing").
  2. A third face: discrimination. The behavioural face can only fail because this backend tells $contains and $regex apart. That property is now pinned in the file — the refusal envelope (code and status, naming $icontains) paired with $contains still answering on the same fixture. Without it, the day driver-sql re-aliases $regex the behavioural pins would quietly become the always-green asset [finding] #5704 收口后仓内新增一个 driver-memory 测试消费者:plugin-auth/auth-contains-filter.test.ts(#5710 pin 测试,与 #5704 程序不变量的张力,请分诊裁处置) #5830 refused to create, and nothing would say so.
  3. A control read on the ^ case. expect(rows).toEqual([]) is the one assertion an empty seed, a missing table or a dropped predicate all satisfy. It now sits next to a contains('a') read over the same fixture, so the case says the store is live and the predicate really selects.
  4. devDep removal@objectstack/driver-memory out of plugin-auth/package.json (zero consumers left) plus the lockfile ripple. pnpm install --frozen-lockfile passes.
  5. Comment revisions — the runtime driver-split pin's [#5830] paragraph, and the sibling coverage test's now-stale "do not finish the job by copying this harness onto it".

Reverse verification

Mandatory here, and the direction was predicted before running it: red via the refusal path, not via row differences. Defect restored by flipping the adapter's contains branch back to a bare $regex (taken out afterwards with git checkout --, never git stash).

❯ src/auth-contains-filter.test.ts (9 tests | 5 failed)
  × emits `$contains`, never a bare `$regex`, for a `contains` search
  × does not read `.` as a wildcard — `a.b` matches `a.b`, not `axb`
  × does not read `^` as an anchor
  × matches a value that is not a legal regex, instead of failing on it
  × answers an ordinary metacharacter-free search unchanged

AssertionError: expected { name: { '$regex': 'a.b' } } to deeply equal { name: { '$contains': 'a.b' } }

Error: Filter operator "$regex" on field "name" is RETIRED and is no longer
evaluated by this driver. Write "$icontains" instead. ...
  ❯ retiredFilterOperatorError ../../drivers/driver-sql/src/sql-driver.ts:754:10
  ❯ _SqlDriver.applyFilterCondition ../../drivers/driver-sql/src/sql-driver.ts:7521:31
  ❯ Object.find src/objectql-adapter.ts:430:37
  ❯ findMany src/objectql-adapter.ts:527:42

Contract face plus all four behavioural pins, every behavioural one through retiredFilterOperatorError. Fix restored: 9 passed.

Reported honestly rather than to the template's shape: the third face stays green under this mutation, and that is correct — it asserts a property of the backend, not of the adapter, and it is precisely what makes the other four go red. A face-3 that moved when the adapter changed would be testing the wrong thing. Stating it here because "every new assertion went red" would have been the tidier sentence and the false one.

Tests and gates

  • pnpm --filter @objectstack/plugin-auth test39 files, 974 passed
  • pnpm --filter @objectstack/plugin-auth typecheck — clean
  • pnpm --filter @objectstack/runtime exec vitest run src/sandbox/undeclared-field-write-driver-split.integration.test.ts — 6 passed
  • Gate list enumerated from .github/workflows/lint.yml and run one by one, not picked from memory. ESLint job: all 31 steps PASS (pnpm lint, check:slot-lookup, check:query-options-erasure, check:verify-stand-in, check:nul-bytes, check:doc-authoring, check:docs-audit-scope, check:role-word, check:quick-reference-counts, check:adr-anchors, check:org-identifier, check:authz-resolver, check:service-providers, check:route-envelope, check:error-code-casing, check:wildcard-fallthrough, check:meta-type-normalized, check:init-service-contract, check:durability-log-level, check:startup-registry-verdict, check:objectui-changeset, check:release-notes, check:release-body, check:node-version, check:workflow-status-functions, check:shard-attestation, check:published-files, check:engine-double-contract, check:resume-authority-declared, check:merge-driver, check:spec-parsed-alias). TypeScript job: all PASS, including the full workspace build, turbo typecheck (120 tasks), check:driver-conformance, check:type-check-coverage, check:type-check-debt, the spec check family, examples + downstream-contract typecheck, and check:i18n / check:i18n-coverage. Also ran the gates other workflows own: check:override-consistency (validate-deps), check:console-sha (ci), and the four spec-liveness-check gates.
  • check:driver-conformance and pnpm install --frozen-lockfile called out specifically because of the devDep removal — both green.
  • Self-scanned the touched files beyond the gate for raw control bytes (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]') — no hits.

Changeset

None, deliberately. This PR is tests + comments + the removal of a devDependency. devDeps are not installed by consumers of @objectstack/plugin-auth, so nothing about the published package changes and there is no user-visible behaviour to describe in release notes. skip-changeset applied.

Out-of-scope finding, filed not fixed

The runtime pin's anchor comment claimed to be "the only PERMANENT test consumer of driver-memory in the repository", and that census had already expired independently of this PR: #6468 (PR #6553) added packages/runtime/src/autonumber-seed-cross-side-parity.integration.test.ts, in the same package, importing the driver for a reason that reads as permanent as the ruled file's — and outside #5704's ruling.

That matters because the census is the enforcement: no gate counts driver-memory consumers, so the comment is the only thing telling the next agent what a grep hit means. Filed as #6664 (finding, no pm:queue) with the three dispositions and a recommendation. This PR does not fix it — it only stops the comment asserting something known to be false: the sentence now claims a ruling about this file instead of a repo-wide count, and points at #6664.

Scope note

The dispatch's file surface was packages/plugins/plugin-auth/** + pnpm-lock.yaml, while the issue's step 3 and the dispatch's own deliverable list both require the comment revision in undeclared-field-write-driver-split.integration.test.ts — which lives in packages/runtime, not plugin-auth (the claim comment records the path as plugin-auth's; that is where it actually is). The edit there is comments only, no code, and it is the named deliverable, so it is included and flagged here rather than silently taken or silently skipped. No driver package and no packages/spec file is touched.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BM1tNf5U3nEbHKR4fo5qVQ


Generated by Claude Code

… `$regex` is refused (#5893)

`auth-contains-filter.test.ts` pins #5710's `contains` -> `$contains` flip. Its
behavioural face ran on `InMemoryDriver` because, when #5830 measured it,
driver-sql routed `$regex` through the same `applyContainsLike` as `$contains`
— a SQL witness answered the defect and the fix identically, so migrating would
have produced pins that pass because nothing distinguishes them.

#5702 (PR #6549) deleted that fallthrough and retired the spelling: driver-sql
now refuses `$regex` by name in the ADR-0112 envelope (INVALID_FILTER / 400).
The expiry condition #5830 wrote for this card is met, so the file moves onto
the PR #5880 harness (SqlDriver + better-sqlite3 `:memory:` + real DDL via
initObjects) and witnesses its defect through the refusal.

- migrate the behavioural face; measured identical answers value-for-value
- add a discrimination face: the backend refuses `$regex` (code AND status,
  naming `$icontains`) while still answering `$contains` — the property that
  lets the behavioural pins fail at all, so this cannot silently rot back into
  an always-green pin
- strengthen the `^` case with a control read, since "no rows" is the one
  answer a broken query and a correct one can both produce
- drop plugin-auth's `@objectstack/driver-memory` devDep (now zero consumers)
  + lockfile ripple
- revise the census comments in the runtime driver-split pin and the sibling
  coverage test, which still said this file must not be migrated

Reverse-verified: restoring the defect turns 5 of 9 cases red — the contract
face plus all four behavioural pins, every one of them via the refusal path
rather than a row difference.

The runtime pin's "only permanent test consumer in the repository" census had
also expired independently of this change (#6468 added a second importer in the
same package); it now claims a ruling about itself rather than a repo-wide
count, and the second file's disposition is filed as #6664.

Fixes #5893

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BM1tNf5U3nEbHKR4fo5qVQ
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 8, 2026 9:29am

Request Review

@github-actions github-actions Bot added the size/m label Aug 8, 2026
@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 8, 2026 — with Claude
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-auth.

10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx (via @objectstack/plugin-auth)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-auth)
  • content/docs/kernel/contracts/cache-service.mdx (via @objectstack/plugin-auth)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/authentication.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/sso.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-auth)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

auth-contains-filter.test.ts 的见证后端迁 sqlite 排在 #5702 之后 —— #5830 裁决 C 的到期单

2 participants