Pure retention policy: decide which annotations have aged out.
Only terminal annotations (:resolved/:dismissed) can expire, and only once
older than the configured TTL — measured from resolved_at, falling back to
inserted_at. Open annotations (:pending/:acknowledged) are never expired
regardless of age, so unhandled feedback is never silently dropped.
This module holds no state and reads no clock: callers pass now, which keeps
the policy trivially testable and lets the orchestrator run it on hydrate.
Summary
Functions
Split annotations into {kept, expired_ids} for the given ttl and now.
Types
@type ttl() :: :infinity | {pos_integer(), :second | :minute | :hour | :day}
Retention window; :infinity disables expiry.
Functions
@spec partition([Annotai.Annotation.t()], ttl(), DateTime.t()) :: {[Annotai.Annotation.t()], [String.t()]}
Split annotations into {kept, expired_ids} for the given ttl and now.
Order within kept is preserved. ttl: :infinity keeps everything. An
annotation whose timestamp can't be parsed is kept (never delete what we can't
date).