- Shipped
- August 26, 2026 at 3:13 AM UTC
- Author
- Kamo
- Commit
- 2a86618
The account timeline I added last commit was a way around the per-lead read gate. A member refused a lead's messages on the page that names the lead could open the account it hangs off and read them there. That is fixed here, and the tests are mutation-proven: bypassing the gate fails three of them. The rule itself moves into LeadCommsReadGate, because there is now more than one way in — the lead timeline asks directly, the account timeline asks once per lead it rolls up. Two copies would drift, and the direction they drift in is a member reading an enquiry they were refused. LeadCommunicationsController keeps its response-building and delegates the decision. The gate runs BEFORE the rollup cap. Capping first would let leads the reader may not see consume the budget and push readable ones out — a timeline that is short for a reason nothing on screen explains. restrictedOwners is reported separately from rollupTruncated because they mean different things to whoever is reading: one is "this account has more leads than we roll up", the other is "you are not allowed to see some of them". LeadCommunicationsService now takes the owners rather than expanding them itself: it never sees the caller, so it cannot make a rights decision. Passing an ACCOUNT to the single-subject overload throws rather than degrading, because both degradations are wrong and one of them is a disclosure — answering with the account's own rows loses most of the history, answering with all of them skips the gate. Also adds the e-mail body endpoint the timeline component needs to open a message. The entry is re-checked against the permitted owners: an entry id alone must not be enough, or any account the caller can open would satisfy the gate while the entry belonged to a lead they were refused.