symfonic.services.conversation.notification¶
notification ¶
Tenant notification and export policy for expiring state.
Expiry is only "explicit rather than silent" if somebody is told before it happens and can take their data with them. This module is the tenant-facing half of the horizon: who is holding state that is about to expire, what they should be told, and what they may export while the window is still open.
The export cut-off is deliberate. Offering export after the cutoff would make the horizon advisory, and an advisory horizon never actually retires anything.
ExpiryNotice
dataclass
¶
ExpiryNotice(tenant_id: str, thread_id: str, checkpoint_id: str, deadline: datetime, support_route: str)
What one tenant is told, carrying no conversation content.
ExportReceipt
dataclass
¶
Proof that a tenant's expiring state was handed back before expiry.
TenantNotificationPolicy ¶
TenantNotificationPolicy(*, registry: CheckpointRegistry, horizon: CalendarHorizon, export: Callable[[CheckpointRef], str] | None = None)
Decides who is notified, and what may still be exported.
Source code in src/symfonic/services/conversation/notification.py
expiring ¶
Refs the horizon would refuse once the cutoff passes.
Source code in src/symfonic/services/conversation/notification.py
export_expiring ¶
Export everything still inside the window. Nothing after it.
Same attribution rule as :meth:notices, for a stronger reason: a
receipt carries the state itself, so handing one to a guessed tenant
is a cross-tenant data release rather than a mis-addressed warning.
Source code in src/symfonic/services/conversation/notification.py
notices ¶
Notices for the current moment; empty outside the notice window.
Notifying earlier would train tenants to ignore the notice, and
notifying after the cutoff would be an obituary, not a warning.
Refs listed by :meth:unattributed are skipped: a notice addressed to
a guessed tenant is a cross-tenant disclosure, not a warning.
Source code in src/symfonic/services/conversation/notification.py
unattributed ¶
Expiring refs whose owning tenant cannot be named.
A thread key the legacy path wrote under a separator-bearing tenant
parses to a prefix of that tenant, so addressing a notice from it
would hand one tenant another tenant's thread and checkpoint ids.
Those refs are held back here rather than mis-addressed โ and rather
than dropped, because an expiry nobody can be told about is exactly
the silent expiry the horizon exists to prevent. The operator route is
to record the owning tenant on the ref (tenant_id), after which it
notifies normally.