Contract 4 of the 9 S2 prerequisites (webui-audit REPORT.md), first on the critical path per the T10 ruling (Q1 = O1, better-auth owns identity per D10).
Covers: system of record, registration gating (registration_mode open/invite/closed), the bootstrap/first-admin invariant folded in from #1430 (all-writer coverage or DB enforcement, pool safety, D4 re-runnability, atomic outcome — each constraint traces to PR #1431's review blockers), OIDC JIT provisioning, claim mapping, account-linking trust, deactivation propagation, and verification requirements (real-Postgres concurrency test for the invariant).
One embedded ruling: registration defaults to closed after bootstrap (§2.2).
Contract 4 of the 9 S2 prerequisites (webui-audit REPORT.md), first on the critical path per the T10 ruling (Q1 = O1, better-auth owns identity per D10).
Covers: system of record, registration gating (registration_mode open/invite/closed), the bootstrap/first-admin invariant folded in from #1430 (all-writer coverage or DB enforcement, pool safety, D4 re-runnability, atomic outcome — each constraint traces to PR #1431's review blockers), OIDC JIT provisioning, claim mapping, account-linking trust, deactivation propagation, and verification requirements (real-Postgres concurrency test for the invariant).
One embedded ruling: registration defaults to closed after bootstrap (§2.2).
Refs #1430. Base: next.
Addresses the revision-2 re-review residuals and new findings:
- emailVerified joins the canonical set; defined reset rule on email change
- external-principal uniqueness moved to (issuer, subject)
- 'usable login method' defined for unlink refusal
- shipped delete affordances (web admin, mosaic CLI) must be removed or
disabled with a defined visible state; deactivation exposed instead
- admin-creation switch dropped in favor of plain admin authorization
- verification matrix extended: IdP removal, forward-auth non-use,
first-admin SSO, wizard-recorded JIT choice, step-up positive/expiry,
issuer-aliasing witness, UI/CLI delete assertions, docs observable
APPROVED — rev-code-01, pinned to head 339e147f775efb0e2f57905ce45db66dbad3f761 (re-verified unmoved immediately before posting; matches fred's corrected sha).
Scope: doc-only, single file docs/requirements/identity-lifecycle.md (+328 lines, four commits). Verified independently of the three model reviews; every codebase-factual claim in the contract was measured by me against the tree at this head:
§2 current state:emailAndPassword: { enabled: true } with no gating confirmed (packages/auth/src/auth.ts:42); default role member confirmed (additionalFields + admin plugin defaultRole: 'member').
§1.2: generic-OAuth wiring confirmed — sso.ts builds provider configs, createAuth passes them to genericOAuth() (auth.ts:14-27).
§6.1: the (provider_id, account_id) index is non-unique today (packages/db/src/schema.ts:87, migration 0003); accounts.issuer exists since migration 0017 (nullable), so the (issuer, subject) unique-constraint requirement is implementable without schema invention.
§7.3: the hard-delete endpoint is live (@Delete(':id') in admin.controller.ts:131 → db.delete(usersTable)), and both shipped delete affordances exist exactly as named (web admin/page.tsx:138 handleDelete; CLI packages/mosaic/src/commands/auth.ts:245 'delete <id>').
§7.3 FK fan-out: counted myself — exactly 21 FK constraints to users.id across 19 referencing tables in packages/db/src/schema.ts. Claim exact.
§7.1:admin.guard.ts contains no banned check (claim exact); ban route is a user-row update only (requireUpdated(..., { banned: true, ... }), no session revocation).
§8.1: the named test pattern exists (connector-lease.postgres.integration.test.ts) and ci-postgres serves the test CI step (.woodpecker/ci.yml:215,239); the one-pooled-handle caveat is consistent with createDb (single postgres() handle, DB_POOL_MAX, packages/db/src/client.ts:13-18).
§1.4: REQ-TEN-001 / REQ-ID-001 exist in the native-kanban SOT corpus (KBN-010). §5.1:users.emailVerified exists (schema.ts:28).
Gates run by me in a fresh worktree at this head: format:check rc=0 with discriminating control (parent e595855a red under pinned prettier, head green — the style commit is necessary and effective); web-deps build rc=0; typecheck rc=0 (45/45 incl. preflight); lint rc=0 (25/25).
Document quality: internal cross-references (§5.1↔§6.4, §6.5↔§8.5, §2.3↔§3) are consistent; the rev-2/rev-3 revision notes map to the lane review files; every MUST has a bounded observable in §8. One decision is explicitly carved out for ratification (§2.2 default closed) — correctly surfaced, not silently embedded.
Non-blocking notes:
[S1] §8.1's parenthetical on the connector-lease pattern is accurate but terse; when implementing, the two-connection harness should state whether it uses two createDb handles or two raw postgres() clients (pool semantics differ).
CI note: pipelines 2807/2812 killed, 2815 canceled, 2816 running on this head at review time (queue congested). Merge must wait for 2816 terminal green.
**APPROVED — rev-code-01, pinned to head `339e147f775efb0e2f57905ce45db66dbad3f761`** (re-verified unmoved immediately before posting; matches fred's corrected sha).
Scope: doc-only, single file `docs/requirements/identity-lifecycle.md` (+328 lines, four commits). Verified independently of the three model reviews; every codebase-factual claim in the contract was measured by me against the tree at this head:
1. **§2 current state:** `emailAndPassword: { enabled: true }` with no gating confirmed (`packages/auth/src/auth.ts:42`); default role `member` confirmed (additionalFields + admin plugin `defaultRole: 'member'`).
2. **§1.2:** generic-OAuth wiring confirmed — `sso.ts` builds provider configs, `createAuth` passes them to `genericOAuth()` (`auth.ts:14-27`).
3. **§6.1:** the `(provider_id, account_id)` index is non-unique today (`packages/db/src/schema.ts:87`, migration 0003); `accounts.issuer` exists since migration 0017 (nullable), so the (issuer, subject) unique-constraint requirement is implementable without schema invention.
4. **§7.3:** the hard-delete endpoint is live (`@Delete(':id')` in `admin.controller.ts:131` → `db.delete(usersTable)`), and both shipped delete affordances exist exactly as named (web `admin/page.tsx:138 handleDelete`; CLI `packages/mosaic/src/commands/auth.ts:245 'delete <id>'`).
5. **§7.3 FK fan-out:** counted myself — exactly **21** FK constraints to `users.id` across **19** referencing tables in `packages/db/src/schema.ts`. Claim exact.
6. **§7.1:** `admin.guard.ts` contains no `banned` check (claim exact); ban route is a user-row update only (`requireUpdated(..., { banned: true, ... })`, no session revocation).
7. **§8.1:** the named test pattern exists (`connector-lease.postgres.integration.test.ts`) and `ci-postgres` serves the `test` CI step (`.woodpecker/ci.yml:215,239`); the one-pooled-handle caveat is consistent with `createDb` (single `postgres()` handle, `DB_POOL_MAX`, `packages/db/src/client.ts:13-18`).
8. **§1.4:** REQ-TEN-001 / REQ-ID-001 exist in the native-kanban SOT corpus (KBN-010). **§5.1:** `users.emailVerified` exists (`schema.ts:28`).
Gates run by me in a fresh worktree at this head: `format:check` rc=0 with discriminating control (parent `e595855a` red under pinned prettier, head green — the style commit is necessary and effective); web-deps build rc=0; `typecheck` rc=0 (45/45 incl. preflight); `lint` rc=0 (25/25).
Document quality: internal cross-references (§5.1↔§6.4, §6.5↔§8.5, §2.3↔§3) are consistent; the rev-2/rev-3 revision notes map to the lane review files; every MUST has a bounded observable in §8. One decision is explicitly carved out for ratification (§2.2 default `closed`) — correctly surfaced, not silently embedded.
Non-blocking notes:
- [S1] §8.1's parenthetical on the connector-lease pattern is accurate but terse; when implementing, the two-connection harness should state whether it uses two `createDb` handles or two raw `postgres()` clients (pool semantics differ).
CI note: pipelines 2807/2812 killed, 2815 canceled, **2816 running on this head at review time** (queue congested). Merge must wait for 2816 terminal green.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Contract 4 of the 9 S2 prerequisites (webui-audit REPORT.md), first on the critical path per the T10 ruling (Q1 = O1, better-auth owns identity per D10).
Covers: system of record, registration gating (registration_mode open/invite/closed), the bootstrap/first-admin invariant folded in from #1430 (all-writer coverage or DB enforcement, pool safety, D4 re-runnability, atomic outcome — each constraint traces to PR #1431's review blockers), OIDC JIT provisioning, claim mapping, account-linking trust, deactivation propagation, and verification requirements (real-Postgres concurrency test for the invariant).
One embedded ruling: registration defaults to closed after bootstrap (§2.2).
Refs #1430. Base: next.
APPROVED — rev-code-01, pinned to head
339e147f775efb0e2f57905ce45db66dbad3f761(re-verified unmoved immediately before posting; matches fred's corrected sha).Scope: doc-only, single file
docs/requirements/identity-lifecycle.md(+328 lines, four commits). Verified independently of the three model reviews; every codebase-factual claim in the contract was measured by me against the tree at this head:emailAndPassword: { enabled: true }with no gating confirmed (packages/auth/src/auth.ts:42); default rolememberconfirmed (additionalFields + admin plugindefaultRole: 'member').sso.tsbuilds provider configs,createAuthpasses them togenericOAuth()(auth.ts:14-27).(provider_id, account_id)index is non-unique today (packages/db/src/schema.ts:87, migration 0003);accounts.issuerexists since migration 0017 (nullable), so the (issuer, subject) unique-constraint requirement is implementable without schema invention.@Delete(':id')inadmin.controller.ts:131→db.delete(usersTable)), and both shipped delete affordances exist exactly as named (webadmin/page.tsx:138 handleDelete; CLIpackages/mosaic/src/commands/auth.ts:245 'delete <id>').users.idacross 19 referencing tables inpackages/db/src/schema.ts. Claim exact.admin.guard.tscontains nobannedcheck (claim exact); ban route is a user-row update only (requireUpdated(..., { banned: true, ... }), no session revocation).connector-lease.postgres.integration.test.ts) andci-postgresserves thetestCI step (.woodpecker/ci.yml:215,239); the one-pooled-handle caveat is consistent withcreateDb(singlepostgres()handle,DB_POOL_MAX,packages/db/src/client.ts:13-18).users.emailVerifiedexists (schema.ts:28).Gates run by me in a fresh worktree at this head:
format:checkrc=0 with discriminating control (parente595855ared under pinned prettier, head green — the style commit is necessary and effective); web-deps build rc=0;typecheckrc=0 (45/45 incl. preflight);lintrc=0 (25/25).Document quality: internal cross-references (§5.1↔§6.4, §6.5↔§8.5, §2.3↔§3) are consistent; the rev-2/rev-3 revision notes map to the lane review files; every MUST has a bounded observable in §8. One decision is explicitly carved out for ratification (§2.2 default
closed) — correctly surfaced, not silently embedded.Non-blocking notes:
createDbhandles or two rawpostgres()clients (pool semantics differ).CI note: pipelines 2807/2812 killed, 2815 canceled, 2816 running on this head at review time (queue congested). Merge must wait for 2816 terminal green.