# Changes other apps need

The register of every change another app needs to work with Atrium, or a fault found in another app while building Atrium. **Nothing here is
done from an Atrium session** (the Architect, 9 November 2026: "This session should be wholly focused on the atrium. No other product
modification. Record all modifications required in other app for later action."). Once Atrium is done and deployed, the products are taken
one after the other, each in a session started in its own folder, and the items below become that product's plan.

Each item names where it was found and the plan that says why. Add to it whenever an Atrium plan or review finds something; strike an item
through with the date and commit when the product's own session does it.

---

## Every product (Campus, Desk, Loop, Books; TuteBridge when it joins)

From plan LG (Legatus, the client library). The library's reference is `reji/docs/reference/legatus.md`; its README is the install guide.

1. **Install Legatus** (`atrium/legatus`, from `packages/legatus` in Atrium's repository until it has its own, LG-1); publish its config and
   run its two migrations (`legatus_session`, `legatus_entitlement`).
2. **The organisation on the tenant** (AF-4): a column holding Atrium's organisation ID (`org_…`) on the product's tenant, and
   `Legatus::organisations(fn (Request $r) => …)` returning the current tenant's.
3. **The account on the user** (IA §9 step 7, CL): a unique, nullable column holding Atrium's account ID (`acc_…`) on `users`, and
   `Legatus::users(fn (Identity $who, string $org) => …)` returning the local user for it (or `null`: not known here). The product decides
   whether someone Atrium knows but the product doesn't gets a user made at first sign-in.
4. **Sign-in through `id`**: the sign-in page's button goes to `route('atrium.sign-in')`, and the page shows `atrium_error` in the product's
   own words; the product's sign-out is a POST that calls `Legatus::signOut()`;
   the product's own password, code, passkey and social sign-in retire once proved, behind a switch, one tenant first (policy P4). The
   product's **Service keeps its local sign-in** (CL-14).
5. **The middleware**: the recheck joins the `web` group by itself (set `ATRIUM_RECHECK_GROUP` if the product's signed-in pages use
   another); `atrium.password` where the product asks for a password today (step-up, `ATRIUM_PASSWORD_MINUTES`). A product whose
   tenant is found in its own middleware group names it in `atrium.middleware`.
6. **The server and its keys**: the server made in Atrium's console, its key in `ATRIUM_KEY`, `server:sign-in` run for its client
   (`ATRIUM_CLIENT_ID`, `ATRIUM_CLIENT_SECRET`, callback path), every live tenant recorded as an `instance` with its host, and its webhook
   address added with the events it needs (at least `account.signed_out`, `membership.ended`, `account.merged`, `account.changed`,
   `entitlement.granted`, `entitlement.changed`, `domain.changed`, `tenant.status_changed`).
7. **Listeners** for `WebhookReceived`: `account.merged` (move the local user to the new account ID), `account.changed` (read the details
   again), `domain.changed` (start or stop serving a verified address), `tenant.status_changed`.
8. **Adding people** through `accounts()->add(...)` from the product's people screen (IA §7, CL-9), keeping its own detailed role.
9. **Limits from the entitlement** (TB-10): read `Legatus::held($org)` where the product enforces a limit or a feature today, and report its
   counts with `entitlement()->used([...])`. Each product's own billing stays off (TB).
10. **The server's report**: the scheduler running (`atrium:report` every five minutes comes with the package), Laravel's `/up` health
    route kept (Atrium's console asks it every minute), and up to six figures from the product's Service dashboard through
    `Legatus::figures(...)`.
11. **Texts and e-mail through Nuntius** (NS, NU): sends through `notify()`, with a small outbox for when Atrium is away (NS-8), each send
    with a key naming that one message and kept across its retries; e-mail from `notify.<product domain>` once its SES identity is
    verified (NU-7).
12. **Short links** through `links()` where a text carries a link (TL), optional.
13. **`atrium:check`** passing on the product's server before its first sign-in through `id`.

## Campus (`tutecampus`)

- **Faults found while building Atrium, Campus's to fix in Campus:**
  - `stubSettle` (`PaymentController.php:92-96`) settles any pending payment for anyone signed in, so a voucher can be issued without
    paying (plan TE §2.1).
  - Voucher redemption checks the code outside its transaction with no lock (`PublicController.php:206`, `:227`, `:250`), so two people can
    spend one code; no throttle on the route, no audit (M00 §6).
  - A Sailup timeout counts as refused and is refunded, though the text may have gone (`SailupDriver::send`; plan NS §2).
  - A sign-in code is marked sent even when it failed (`SignInCodes::text`; plan NS §2).
  - The pause after 10 wrong codes probably never starts on the database cache (`SignInCodes::countFailure`, `SignInCodes.php:253`; plan CL §3).
  - A code sign-in skips the authenticator app (`PhoneSignInController.php:342`; plan CL §3).
- **The move to Atrium's accounts** (IA §9 step 8): its own plan, with the list in IA §9 ("What Campus's move involves").
- **Its gateway** (NS-9): Campus's texts through Nuntius, on the Architect's say-so.
- **Vouchers to Tessera** (TE): whether Campus's admissions vouchers move, the Architect's decision; new voucher capabilities are built
  in Tessera, not Campus (M00 §6).
- **Its `lookups` table** to TuteCore's Input Fields module (handbook Decision 6), and a fail-closed tenant scope (handbook 1.3, Decision 1).

## Desk (`tutedesk`)

- Launch on sign-in through `id` and Tabula, with no migration and without switching on its own billing (IA §9 step 9).

## Loop (`tuteloop`)

- A real move: phone and password with its own `persons` (IA §9 step 9). Its own SMS driver to Nuntius (M00).

## Books (`tutebooks`)

- No tenants yet: starts fresh on sign-in through `id` (IA §9 step 9).
