docs(auth): compile every README snippet against the shipped surface - #8189
Conversation
Burns down the `packages/auth/README.md` row of `UNGATED_DOCS` in `scripts/check-doc-snippet-types.mjs`: the page's ten `tsx` fences now compile in the gate's covered tier instead of being ledgered. Census on the base, with the gate's own analyzer, matched the ledger row exactly (17 = 1 parse + 15 TS2304 + 1 TS2741). Re-measured after adding only the missing import lines and `declare` stand-ins, the real debt is 2 TS2741, both the same defect: `AuthProviderOptions.authUrl` is required and the Quick Start omitted it. The second one was hidden -- an unresolved `AuthProvider` tag suppresses the prop check on its own element. - Every continuation fence gets its own import line and typed bindings (`declare const X: () => ReactNode`) for the names it uses. - The three-form fence parsed as one JSX expression (TS2657); the elements are now separate statements. Measured: it hid no prop defect. - `authUrl` added beside `client` in the two `AuthProvider` examples -- the spelling every call site in this repository already writes, including `skills/objectui/guides/auth-permissions.md`. - No type was widened, no fragment marker added, no `src/` file touched. Part of #5174 (batch 28) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
Standing down on That check is informational (not in the Generated by Claude Code |
|
Armed —
Landing stroke follows the merge: content probe on re-fetched Generated by Claude Code |
|
LANDED — Merged at 2026-09-07T00:06:00Z as Batch 29 ( Generated by Claude Code |
Part of #5174 (batch 28: packages/auth/README.md)
Burns down the
packages/auth/README.mdrow ofUNGATED_DOCSinscripts/check-doc-snippet-types.mjs. The page's tentsxfences now compile in the gate's covered tier instead of being ledgered.origin/main153d43237(carries PR docs(plugin-ai): compile the README's snippets against the shipped surface, retire its UNGATED_DOCS row #8180, batch 27). Head:7293545be. One commit, no amend, no force-push.packages/auth/README.md(+45/-16) andscripts/check-doc-snippet-types.mjs(-2, the two lines of the ledger row and nothing else).Census — two readings
Both taken with the gate's own exported analyzer (
analyze/compileSnippets), the target forced into the compiled tier and every other ledger row left ungated. No hand-written regex.Reading 1 — ledger-literal, on the base
17 diagnostics, matching the ledger row exactly: 1 parse + 15 undefined-name + TS2741 x1.
authUrlmissing on the AuthProvider element; TS2304LoginPageAuthProviderx2,authClient,AppuseAuthAuthGuardx2,LoginForm,ProtectedContentUserMenucreateAuthenticatedFetchx2DashboarduseAuthThe ledger's canned wording for the parse diagnostic is wrong on this page: it says "blocks fenced
tsthat are bare object literals or elided bodies". This page has notsfence and no object-literal fence. The single parse failure is three sibling JSX elements written as one expression (fence 112). Moot now that the row is deleted, recorded because the wording is shared with other rows.Reading 2 — after imports and stand-ins, in memory
Only the missing import lines plus
declarestand-ins for names the page never defines were added, and fence 112 was given a JSX parent so its parse failure stopped hiding the element checks behind it. Nothing about any prop was touched.authUrlmissingauthUrlmissingThis is the reading the ledger-or-repair decision was made on. The real type debt is 2, not 1: fence 62's
AuthProvidertag was unresolved, and an unresolved JSX tag name short-circuits the prop check on its own element, so it hid a second TS2741 behind its TS2304 — the same mechanism batch 27 measured and wrote into the worklist. The other 15 diagnostics were import shape, not defects.Reading it the other way round: the ledger row's 17 overstated the page's real debt (15 of the 17 were missing imports) while its
TS2741x1understated the type debt by exactly one. Both halves of a mixed row are unreliable, in opposite directions.Measured, not assumed: fence 112's parse failure hid no prop defect. With a parent element added and nothing else changed, the fence reads zero —
onSuccessis a real prop on all three form components.Per-fence decision
Every fence got a typed binding; no fragment marker was added anywhere on this page (0 before, 0 after), and no type was loosened.
authUrladded besideclient;LoginPagetyped asdeclare const LoginPage: () =aReactNodereturner; the URL hoisted to aconstso the client'sbaseURLand the prop are visibly the same string rather than two copies.declare const authClient: AuthClient(the exported type),declare const Appstand-in,authUrladded. Prose above it now states thatauthUrlis required andclientoptional.ProtectedContentstand-in.navigatestand-in typed(to: string) =void.Dashboardstand-in. ItspreviewModeobject literal was already clean.Stand-in spelling is the one already established on this corpus (
packages/app-shell/README.md:59):import type { ReactNode } from 'react'plus adeclare const.The TS2741 decision — README defect, repaired
Decided against the exported type, as ruled.
AuthProviderProps—packages/auth/src/AuthProvider.tsx:119—extends AuthProviderOptions, addingchildren,enabled?,previewMode?: PreviewModeOptions.AuthProviderOptions—packages/auth/src/types.ts:513— declaresauthUrl: string(required, line 515),client?: AuthClient(optional, line 517),onAuthStateChange?,redirectTo?.So the dispatch's framing (that the type refuses one of
client/authUrl) does not hold: both are members. The type refuses nothing the page passed; it requires something the page omitted. The Quick Start and the AuthProvider example passedclientalone, andauthUrlis required.Verdict: README defect, branch A — repaired to the shipped surface. The page used to teach that
clientalone configures the provider. Every call site in this repository writes both, including the published skill guideskills/objectui/guides/auth-permissions.md:29and:328,skills/objectui/guides/testing.md:201, the component's own three JSDoc examples (AuthProvider.tsx:144,:151,:157), and 20-plus tests underpackages/auth/src/__tests__/. The README was the only place teaching the one-prop form.Separate observation, offered as a finding proposal rather than filed (the runtime read is cited, per the dispatch's rule that a type/runtime divergence found by this batch belongs in the PR body and the PM opens its card):
authUrlis required by the type but dead at runtime wheneverclientis supplied. The only read isAuthProvider.tsx:171—externalClient ?? createAuthClient({ baseURL: authUrl })— andauthUrlappears nowhere else in the component (greps at:163,:171,:172only). A caller who already has a client must still pass a URL that is never used. Contract-first would express this as a discriminated union (clientXORauthUrl) so a declaration cannot promise a value the runtime ignores. Out of scope here twice over: it is apackages/auth/src/**change this batch may not make, and the README compiles correctly without it. No card filed by me.Ruling 4 — key surface
The bound does not bite anywhere on this page. No binding here is
BaseSchema-derived, so objectui#7927's hand-classification does not apply and the table collapses to the compiler's own verdict, per fence:AuthProviderProps(AuthProvider.tsx:119) extendingAuthProviderOptions(types.ts:513)previewModevaluePreviewModeOptions(types.ts:411)simulatedRole,simulatedUserName,readOnly,bannerMessage) are excess-checked. All four are declared (:415,:417,:419,:423).AuthGuardProps,LoginFormProps/RegisterFormProps/ForgotPasswordFormProps,UserMenuProps,PreviewBannerPropsA2 confirmed for both types named in the dispatch:
AuthProviderPropsandPreviewModeOptionsare plain props interfaces with no index signature.Consistency check done while here: the
useAuthtable (9 rows) and thePreviewModeOptionstable (6 rows) both match their declarations exactly, and fence 72 destructures all 9 names with zero diagnostics.Ledger decision
The page reads zero after the repairs, so its
UNGATED_DOCSentry is deleted, not rewritten. The gate diff is exactly those two lines and nothing else.Counters on the head, from the gate's own summary:
153d432377293545beThe 10 new compiled blocks are exactly this page's 10 fences. No pin enumerates this row:
scripts/__tests__/check-doc-snippet-types.test.tspins only the rootREADME.mdrow (:600,:601), so no test file needed editing.Strictness region
The region from the
Fence scanningbanner to EOF, 83711 bytes, the ledger sitting above it:153d432372749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b7293545be2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084bByte-identical, and identical to the PR #8158 baseline.
Positive control
Run against the committed tree, under a
trapwith absolute paths.git diff HEAD -- packages/auth/README.md0 bytes; disk blob equals the HEAD blob8e7c95fd2ec32b9aa07443d9f556880e3c1b6bbf.authUrl={authUrl}toauthUrl={123}in the Quick Start fence. Landing proved by anchor counts (anchor 1 to 0, injected text 0 to 1) plus the blob move tod705be3ed1fef0345b4e1f0ddb88dc342b472c85— not by an editor's exit code.[semantic] packages/auth/README.md:39:19 TS2322: Type 'number' is not assignable to type 'string'.git diff HEAD -- path0 bytes AND disk blob back to8e7c95fd2ec32b9aa07443d9f556880e3c1b6bbf(the HEAD blob),git status --porcelainempty.Gates
All pinned to head
7293545beon a clean tree. Exit codes captured by redirect-then-capture, never through a pipe.pnpm check:doc-snippetspnpm exec vitest run scripts/__tests__/check-doc-snippet-types.test.ts scripts/__tests__/check-doc-fence-languages.test.tspnpm exec vitest run scripts/__tests__/(whole directory)pnpm check:doc-typespnpm check:readme-exportspnpm check:doc-fencespnpm type-check:scriptspnpm lint:rootpnpm check:control-bytesgrep -naPcontrol-byte self-scan, both changed pathsnode scripts/check-changeset-presence.mjsnode scripts/check-governed-queue-guard.mjs --test(both paths)pnpm check:entry-guard--build-filter,--concurrency=2)Re-derived against the actual diff (a markdown page plus one
scripts/*.mjs), beyond the dispatched list — all 0:check-doc-component-types.mjs,check-doc-expression-carriage.mjs,check-doc-links.mjs,check-doc-snippet-types.mjs --emit-census,check-shell-escape-residue.mjs,check-lint-coverage.mjs,check-type-check-coverage.mjs,check:doc-example-readers,check:esm-specifiers,check:node-esm-load.One note on the last of those.
pnpm check:node-esm-loadfirst exited 1, and the reason was environmental, not this diff: turbo shares one cache across every worktree of a checkout, so six packages replayed artifacts built in/home/user/objectui-issue-5174-b27(batch 27's worktree) and the gate correctly refused to grade another tree's output. Re-run with--force-build, as the gate's own message instructs, it exits 0 with 37 of 37 gradable entries built by this tree. Worth knowing for any batch that runs it next to a sibling worktree.Live E2E (informational)is red on every branch today for an upstream reason (#7990 / objectstack#16186) and is not this PR's.Generated by Claude Code