# P11 slice 140 — `account-notify` (IRCv3) **Area.** account-notify / SASL track — the direct behavior-cap follow-on to slice 139 (services account identity via `ENCAP * SU`). Slice 139 built the *read* plane (a user carries an account name, surfaced on WHOIS `330` / WHO `%a`); this slice builds the *push* plane. ## Scope When a user's services account changes — i.e. an inbound `: ENCAP * SU [:]` logs `` in or out — deliver `:nick!user@host ACCOUNT ` to every **local** channel co-member of the changed user that negotiated `account-notify`, where `` is the account name on login and the literal `*` on logout (the IRCv3 account-notify wire form). Also push to `extended-monitor` watchers (gated `account_notify` + `extended_monitor`), closing the documented gap in the [`EXTENDED_MONITOR`] doc ("leveva has no account-notify, so that one sub-notification never fires"). Account changes in leveva originate **only** from `ENCAP * SU` (leveva keeps no account DB), so that is the only trigger. The login-on-JOIN case (`extended-join`) and the `@account` message tag (`account-tag`) are *separate* later caps per the framework-vs-behavior rule — out of scope here. ## Mechanism 1. **`cap.rs`** — register the cap (one entry, mirrors `away-notify`): - `pub const ACCOUNT_NOTIFY: &str = "account-notify";` with a doc comment. - A `Capability { name: ACCOUNT_NOTIFY, value: None }` row in `SUPPORTED`. - A `pub account_notify: bool` field on `ClientCaps` + its line in `client_caps()` snapshot. - Amend the `EXTENDED_MONITOR` doc: leveva now *does* push `ACCOUNT` to extended-monitor watchers, so drop the "never fires" caveat. 2. **`s2s/su.rs`** — add a `notify_account` fan-out (mirrors [`s2s::away::notify_co_members`]): - Resolve the changed user's mask from its registry record (`record_of(uid)` → nick/user/host; the registry is the SoT for both local and remote users). If the user is unknown, no-op (nothing visible to notify). - Build `:nick!user@host ACCOUNT ` where `` is the account name on login or `*` on logout (a single middle param, per the account-notify spec — not a trailing). - Deliver to each **local** `co_members(uid)` whose `caps_of(member).account_notify` is set (self is excluded by `co_members`). - `crate::monitor::notify_watchers(ctx, nick, uid, |c| c.account_notify, &wire)` for the extended-monitor watchers that share no channel. - Call it from `apply_encap_su` *after* `set_account`, with the resolved new value (`Some(name)` → that name; `None`/logout → `*`). The invalid-account branch stays a no-op and notifies nothing (state untouched). The caller's verbatim split-horizon relay is intact. ## Divergences (documented, leveva-native) - **Single-server-local push effect.** Only *our* local cap co-members see the `ACCOUNT` line; the rest of the net learns of the login via the onward `ENCAP * SU` relay (each server applies + does its own account-notify push to its locals). The standing `s2s::relay` single-uplink divergence. - **No `leveva-integration` differential** — the C 2.11 oracle has no `SU`/account concept and emits no `ACCOUNT`, so there is nothing to pin against (as with slice 139). - **Logout wire form is `*`** (account-notify convention), distinct from WHO `%a`'s `0` (WHOX not-logged-in convention). Both are leveva-native; the read plane (139) and push plane (140) use the form each spec mandates. - **Trigger is `SU`-only.** A registered local client cannot self-`ACCOUNT` (leveva has no client SASL yet); the only authority is a services `SU` broadcast. ## Tests (TDD — RED first) + fuzz - **Unit (`s2s/su.rs`):** - `su_login_notifies_a_local_capable_co_member` — peer SU-login → a local `account-notify` co-member receives `:rover!… ACCOUNT RoverAcct`. - `su_logout_notifies_with_a_star` (inverse) — a subsequent logout pushes `… ACCOUNT *`. - `su_does_not_notify_a_non_cap_co_member` — a co-member without the cap gets nothing (state still applies). - `su_for_unknown_uid_notifies_nothing` — unknown target → no push, no panic. - `su_with_invalid_account_does_not_notify` — invalid name → state + push both untouched. - **Golden (`golden_s2s_account.rs`, extended):** add a second local client `carol` who `CAP REQ :account-notify`s and joins `#hub`; assert she receives the live `ACCOUNT RoverAcct` on login and `ACCOUNT *` on logout (a new snapshot section; the existing WHOIS/WHO assertions and their snapshot are unchanged — account defaults to `None`). - **Proptest (`account_proptest.rs`, extended):** a property that drives arbitrary `SU` login/logout sequences through `s2s::dispatch` with a capable co-member attached and asserts the delivered `ACCOUNT` param always tracks the last valid account (name on login, `*` on logout), never panics, and a non-`SU` ENCAP delivers nothing. ## Gate `cargo test -p leveva` (touched binaries) green; new su unit tests + extended golden snapshot + extended proptest pass; `cargo clippy -p leveva --tests` clean; `cargo build --workspace` 0 warnings; no existing snapshot churned.