Skip to content

bug(skills): the auth-permissions.md useAuth example reads user.name behind an isAuthenticated guard that guest mode and preview mode hardcode true #9350

Description

@os-tesla

Found while repairing objectui#9311 (the unguarded dataSource read in data-integration.md). Same class — published guidance reading a member the contract declares as possibly absent — but a different guide and a different member, so it is filed rather than folded in: objectui#9311's PR is a governed-surface PR parked for human merge, and its diff is deliberately one file.

⛔ Not claimed, not edited. Published skills/** is a governed surface.

The defect

skills/objectui/guides/auth-permissions.md, the fence under "### useAuth hook" (fence opens at :38, read on origin/main = a5921a0f8). Written out in prose because JSX tag shapes do not survive being stored in an issue body — TAGNAME stands for each element:

const { user, isAuthenticated, isLoading, error, signOut } = useAuth();

if (isLoading) return SPINNER;
if (!isAuthenticated) return LOGINBUTTON;

// ... then, inside the returned markup:
Hello {user.name}

user.name is read with isAuthenticated as its only guard.

Why that guard does not hold — and this is the part worth reading

AuthContextValue declares user: AuthUser | null (packages/auth/src/AuthContext.ts). isAuthenticated is not derived from user in two of the three modes (packages/auth/src/AuthProvider.tsx):

const isAuthenticated = (enabled && !isPreviewMode)
  ? user !== null && session !== null
  : true;

⇒ in guest mode (enabled: false) and in preview mode, isAuthenticated is hardcoded true while user stays null. So the early return does not imply a user, and user.name throws a TypeError in exactly those two modes — the ones a reader prototyping without an auth backend is most likely to be in.

⭐ The repository's own shipped component already knows this. packages/auth/src/UserMenu.tsx guards both:

if (!isAuthenticated || !user) {
  return null;
}

The published guidance guards one. That asymmetry is the finding: this is not a compiler-pedantry item, it is the shipped code and the published example disagreeing about a contract the shipped code reads correctly.

Failing probe, named

node scripts/check-skill-examples.mjs --measure (judges every candidate fence, marked or not) on origin/main:

[semantic]  skills/objectui/guides/auth-permissions.md:49:20  TS18047: 'user' is possibly 'null'.

Corpus-wide that is the only possibly-null/undefined diagnostic in skills/ + .claude/skills/ besides objectui#9311's — measured, 121 candidate ts fences, 97 judged.

⚠️ Why the fence is green today, and why marking it is not the whole repair

The fence carries no os:check opt-in marker, so check-skill-examples.mjs does not judge it: the defect is published and uncompiled, which is the class that gate's own header calls "looks like enforcement, isn't".

⛔ But adding the marker is not the fix on its own. The same fence also fails on three identifiers it never declares (the spinner, login-button and button placeholders), so it is a FRAGMENT by construction, and marking it would turn the gate red for reasons that are not this defect. The repair is the guard plus prose saying why a signed-in-looking context can still have no user; whether the fence is then made self-contained enough to mark is a second, separable question.

Suggested repair

Guard both members, the way UserMenu.tsx does, and say why in the prose around the example (AGENTS.md requires the prose to move with a changed example):

if (!isAuthenticated || !user) return LOGINBUTTON;

Refs: objectui#9311 (same class, the dataSource member) · objectui#7912 (the narrowing ruling that made objectui#9311's instance visible)

Filed by an os-dev seat while working objectui#9311, using Claude Code, session session_01UzHd6hDYatoDn17BuwKxnZ.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions