- apps/api/src/auth/user.ts: upsertUser step-5 insert now sets claimed=true
for all OIDC-created users. An identity-bound OIDC user is never a pending
wizard bootstrap user; explicit claimed=true prevents ambiguity with the
(oidcIss IS NULL AND claimed=false) sentinel used by the TOCTOU guard and
isSetupLocked. First-login-claims path is unaffected (it updates a
pre-existing oidcIss=null row; this change only touches the fresh insert).
- apps/api/src/routes/setup.ts: TOCTOU guard in POST /credential now queries
WHERE oidc_iss IS NULL AND claimed = false FOR UPDATE, matching the exact
definition of a pending wizard bootstrap user. This provides defense-in-depth
against any future path that could produce claimed=false OIDC rows.
- apps/api/tests/auth/user.test.ts: new WR-01 test asserts that the fresh
OIDC insert sets claimed=true in the values passed to db.insert().
- apps/api/tests/routes/setup.test.ts: new WR-01 integration test seeds an
OIDC user with claimed=false (oidcIss NOT NULL) and verifies POST /credential
still succeeds (guard ignores the OIDC row, only counts local wizard rows).
All 402 API tests, 253 PWA tests, and typecheck pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- first user with zero admins → is_admin=true in INSERT values
- subsequent user with admin present → is_admin=false in INSERT values
- existing user re-upsert → is_admin unchanged (early-return path, no insert)
- update existing color tests to accommodate new 4-select flow order
Gate 2 A3 fail: a second member (amelia) got the same color as the first (luc),
both #E8734A. Color was assigned by COUNT(*) % palette; a deleted spike user
shifted the count so two live members landed on the same slot. Replace with
'first palette color not already in use by another user' (fall back to count
round-robin only once the palette is exhausted) — guarantees distinct, stable
colors for up to palette length members. +1 regression test (deletion frees a
slot → next member fills it, no collision).
- Replace it.todo stubs with real failing tests (RED gate)
- Tests cover palette[0] first user, palette[1] second user, idempotent
re-upsert, iss+sub identity key, full row shape
- Tests fail: src/auth/user.ts does not exist yet