# CL1 · Accounts without organisations: the audit before the sweep

**Status:** A1 to A5 agreed by the Architect, 7 November 2026, and built the same day. One refinement while building: the person's phone moved to the account entirely, since it was only ever one they confirmed and the phones a workspace typed were already on its memberships, so `person.phone` went with `phone_verified_at` (A3).

## What CL1 does

The account (`user`) gets an `acc_…` ID and holds its sign-in identifiers as rows: phones, e-mails and a username, each verified one on one account (CL-1, CL-2, CL-3, CL-17). An account no longer needs an e-mail, a password or an organisation (IA-4). Today's sign-in keeps working; the new one is CL2.

## What reads the account's e-mail or the person's phone today

Read in the code, 7 November 2026.

| Area | Where | What it does with them |
|---|---|---|
| Sign-in | `FortifyServiceProvider`, `SignInPages`, `AccountLock`, `TwoFactor`, `config/fortify.php` | Fortify signs in by `user.email`; the lock and limiters key on it; two-factor is required by a role in a membership, so an account with none can never be required to have it |
| The account | `User`, `AccountController`, `EmailChangeController`, `RequestEmailChange`, `ConfirmEmailChange`, `MeResource` | `user.email` is NOT NULL and unique; its mutator takes a string only; changing it runs through confirmation |
| Joining | `SignUpController`, `SignUps`, `CreateWorkspace`, `SignUp` | Sign-up needs an e-mail and a workspace name, and makes both at once |
| Which organisations an account reaches | `Membership::lets()`, `scopeLetting`, `PersonWorkspaces`, `Acting`, `Seeing`, `Context`, `Recipient`, `MembershipPolicy`, five events | A membership lets an account in only while it holds the account's e-mail (plan SP §5). That stops a wrong match handing someone access, and it shuts out a phone-only account |
| People | `PersonMatcher`, `AddPerson`, `UpdatePerson`, `MergePeople`, `SplitPerson`, `PersonImport`, `Candidate`, `PersonRules`, `PersonController` | A sure match is the account's e-mail or `person.phone` with `phone_verified_at`; merge and split move the account by the e-mail it signs in with |
| Invitations | `InvitePerson`, `AcceptInvitation`, `Invitation`, `InvitationController`, the trigger that closes an invitation when its e-mail changes | By e-mail only, and accepting one makes the account |
| Mail | `Recipient`, `RecipientResolver`, `Outbox`, `SendEmailMessage`, `Notices`, `Preferences`, `TemplateEditor`, four events | Every person is reached at an e-mail |
| Privacy | `ProfilePart`, `EmailPart`, `MembershipPart`, `PersonalData`, `PersonalFields`, `Requests`, `Subject`, `PrivacyRequestController` | Copies and erasure find a person by their e-mail; `password_reset_tokens` are erased by it |
| The Service | `HasServiceIdentity` and the five `service:*` commands | The Service is whoever's e-mail hashes to `SERVICE_IDENTITY` |
| Lists and search | `PersonList`, `AuthEventList`, `EmailLogList`, `PersonKind`, `PersonResource` | Show and search by e-mail and phone |
| Tests | 56 files | Most make accounts with an e-mail |

## Choices to agree

| # | Choice | Recommended | Alternative |
|---|---|---|---|
| **A1** | Where the identifiers live | **A table of rows, `account_identifier`**, as CL-1 decided, because an account may hold a second e-mail kept private (CL-17). **`user.email` stays as a copy of the primary verified e-mail, kept by the database**, and is recorded as derived. Sign-in, mail, matching and the Service identity then read on unchanged, and only what writes an e-mail changes, about fifteen places | Drop `user.email` and change all sixty readers in one go. Purer, and a much larger sweep with more to break |
| **A2** | Which organisations an account reaches | **A membership lets an account in when it holds one of the account's verified identifiers, e-mail or phone, or when the account accepted its invitation or claimed it,** noted on the membership. That keeps plan SP §5's protection, since a wrong match still lets no one in, and opens it to phones | Let any membership on the account's person in, which drops the protection |
| **A3** | The person's confirmed phone | **It moves to the account as its phone identifier,** the one home for it (CL-17). The person keeps a contact phone only, and the matcher matches on verified identifiers | Keep `person.phone_verified_at` beside the new rows: the same fact twice |
| **A4** | An account with no e-mail | **Nothing is mailed to it.** It sees its notices in the app; its codes and security messages go by text, which CL2 builds on Nuntius | Require an e-mail after all, against IA-5 and CL-2 |
| **A5** | The account's ID | **`acc_` before it,** with the column widened and today's IDs given the prefix, as servers' and messages' were (IA §5) | Leave user IDs untyped |

## The one end-to-end test

A phone-only account is made with no organisation, joins one by an invitation sent to its phone, is let in, is removed, and loses it; a second account can't verify the same phone; an e-mail account and a username account work the same way; and the Service and every existing sign-in test still pass.

## Not in CL1

The new sign-in page and codes (CL2), OIDC (CL3), the organisation pages at `id` (CL4) and the account pages (CL6). CL1 is the model they stand on.
