Skip to content

test(plugin-auth): map twoFactor / jwks / deviceCode into D7 collision coverage (#7994) - #8121

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-7994-d7-map-twofactor-jwks-devicecode
Aug 12, 2026
Merged

test(plugin-auth): map twoFactor / jwks / deviceCode into D7 collision coverage (#7994)#8121
os-zhuang merged 1 commit into
mainfrom
claude/issue-7994-d7-map-twofactor-jwks-devicecode

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #7994

Executes the maintainer ruling of 2026-08-12 (Option B), recorded on the card with provenance 「接受你的全部建议。」:

map twoFactor / jwks / deviceCode into MODEL_TO_OBJECT, add the three COVERED_OBJECTS entries, and retire their three #7770 exemptions — D7 collision coverage goes 9 → 12 objects.

That ruling explicitly amends the #7820 ruling's 「保持不动」 line to have scoped that card only, so retiring the three exemptions is the decision here, not a reversal of one.

What changed

One file, packages/plugins/plugin-auth/src/managed-extension-fields.test.ts:

MANAGED_EXTENSION_FIELDS is untouched — zero diff. It doubles as the ADR-0092 D2 write whitelist, so buying D7 coverage by declaring fields on sys_two_factor / sys_jwks would have been a security change wearing a bookkeeping disguise. The new coverage is proven through findCollisions() against a synthetic registry instead.

The acceptance criterion, and why the count is not it

"Coverage goes 9 → 12" is a count, and a test asserting the map has twelve entries derives its expectation from the structure it checks — an assertion that cannot fail. The ruling's own wording sets the real bar: a synthetic collision on each of the three tables must turn the gate red. Predictions were written before each run.

A1 — synthetic collision on the real gate (temporary MANAGED_EXTENSION_FIELDS entries sys_two_factor.secret, sys_device_code.user_code, sys_jwks.private_key, never committed). Predicted red; observed red:

AssertionError: these extension fields collide with better-auth's own schema at the pinned
version: sys_two_factor.secret, sys_device_code.user_code, sys_jwks.private_key
  Tests  2 failed | 23 passed (25)

The counterfactual — the same three collisions against main's unmapped gate. The collision assertion passes green; only the #7770 accounting net fires, saying the three objects "were skipped and proved nothing about their fields". That is the hole this card closes, measured rather than argued.

A2 — can the new pins fail? Deleting the three MODEL_TO_OBJECT entries while keeping the pins. Predicted red; observed red on all three per-table pins (byObject[object] becomes undefined, if (!owned) continue swallows the synthetic field, findCollisions returns []):

× reports a synthetic collision on sys_two_factor (both spellings)
× reports a synthetic collision on sys_jwks (both spellings)
× reports a synthetic collision on sys_device_code (both spellings)
× keeps the twelve mapped models covered, exactly (coverage cannot shrink)
  Tests  6 failed | 19 passed (25)

Each pin asserts both spellings (backup_codes / backupCodes, private_key / privateKey, user_code / userCode), and a companion test keeps them non-vacuous by requiring silence on three plausible extension names these tables do not own.

Verification

  • pnpm --filter @objectstack/plugin-auth test — 45 files, 1069 tests passed (re-run after rebasing onto current main, closure rebuilt first since objectql moved).
  • pnpm --filter @objectstack/plugin-auth typecheck — clean.
  • check:nul-bytes, check:test-source-alias, check:docs-audit-scope, check:query-options-erasure — all green; the erasure ratchet reports no files added against the base.
  • Test-layer type debt measured the way check:type-check-debt measures it (sibling config dropping the test excludes): 108 with this change, 108 on the same commit without it — delta zero, against a recorded ceiling of 131. The single diagnostic in this file (TS1470 on the pre-existing import.meta.url line) is present in both measurements.

Release impact

None — test-only, so this PR carries skip-changeset rather than a changeset ("such a PR releases nothing", per the workflow's own prescription in lint.yml).

Generated by Claude Code


Generated by Claude Code

…#7994)

The 2026-08-12 ruling adopts Option B: map the three models the #7820
widening made derivable, add their COVERED_OBJECTS entries and retire the
three #7770 exemptions. D7 collision coverage goes 9 -> 12 objects.

MANAGED_EXTENSION_FIELDS is untouched: it doubles as the ADR-0092 D2 write
whitelist, and the new coverage is proven with a synthetic registry through
findCollisions() instead of by declaring fields on credential tables.

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

vercel Bot commented Aug 12, 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 12, 2026 4:47pm

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

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

Labels

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.

[Decision] D7 now derives the twoFactor / jwks / deviceCode models — map them and retire their three #7770 exemptions?

2 participants