- Shipped
- August 6, 2026 at 11:54 PM UTC
- Author
- Kamo
- Commit
- 7f3e48e
ONE hr.package_assigned per member per package, never one per document: that is what suppressInvite exists for, and 8 documents x 200 members is 1,600 envelopes for 200 people. The send is one contiguous, transaction-free phase AFTER the assignment rows and the envelopes, because EmailService's send is blocking HTTP into blocking SMTP and interleaving 200 of those with envelope minting stretches a delivery HR is watching a progress state for. Deliver's repair pass and a repeat Deliver both rebuild the same notices and both are refused at the ledger claim rather than by an if — the repair pass exists precisely to re-walk members who were already assigned, so an in-code condition would have to encode "already assigned but possibly not yet emailed", which is the state the ledger already knows. hr.package_voided goes out only for the VOIDED outcome. SKIPPED_EXECUTED must not email — that member finished, and telling them their completed package was withdrawn is both wrong and alarming — and NOT_FOUND must not either, since the id was never this org's and the void service already refuses to resolve a name for it so it cannot become a cross-tenant oracle. Dispatch runs through LegalAfterCommit onto the delivery pool: a serialization retry that rolled the void back would otherwise have already told 200 people their package was cancelled, and there is no unsend. hr.package_completed is a receipt to the member carrying attestedAt and the manifest digest. Called out deliberately: the scope sentence lists assigned and voided, but a canonical template nothing sends is dead weight, and until the filed artifact is downloadable this receipt is the only record the member holds.