Recently-used facet values, ranked by frecency (Spec 022).
Most filtering is repetition — the same three workers, the same two statuses, retyped every time. Surfacing what someone actually used last, at the top of the list, is the cheapest large improvement available to a picker: no new interaction to learn, and it collapses a search into one keystroke for the values that make up the bulk of real use.
Flicker owns no storage
Recency is user data. Where it lives, how long it's kept, and whether it falls under a retention policy are decisions a library shouldn't make on a host's behalf — owning a store would mean owning a migration and a GDPR question. So a host supplies two functions:
recent_values: %{
load: fn facet_key, opts -> [{value, used_at, count}] end,
record: fn facet_key, value, opts -> :ok end
}opts carries :actor and :tenant, so a host can scope storage per user
however it likes. Omitting the config disables the feature entirely — no
group renders and neither function is ever called.
Flicker.RecentValues.Ets ships for the playground and tests, and is
explicitly not durable.
Recency is a hint, never an authorisation bypass
A remembered value is only ever a suggestion of what to offer. It must be resolved through the normal actor-scoped path before display, and anything that no longer resolves — deleted, or no longer readable by this actor — is silently dropped. Not announced, not logged to the user: "you used this before but can't see it now" is itself a disclosure.
Summary
Functions
The default number of recent values shown.
Loads and ranks a facet's recent values through the host's load function.
Ranks raw usage facts by frecency, most relevant first.
Records a value as used, through the host's record function.
Types
The host-supplied storage functions.
@type usage() :: {value :: term(), used_at :: DateTime.t(), count :: pos_integer()}
A raw usage fact, as a host's load returns it.
Functions
@spec default_limit() :: pos_integer()
The default number of recent values shown.
Examples
iex> Flicker.RecentValues.default_limit()
5
Loads and ranks a facet's recent values through the host's load function.
Returns [] when no config is set, when the facet key isn't known to the
store, or when load misbehaves — this is a nicety, and it must never be the
reason a picker fails to render.
Ranks raw usage facts by frecency, most relevant first.
score = count * 0.5 ** (age_in_days / half_life_days)The properties that matter: a value used once today outranks one used twice last month, and a value used constantly stays near the top without any single use dominating. Ties break on recency.
:now is an argument rather than a call to DateTime.utc_now/0, so the decay
curve is testable at fixed times.
Options
:now— the reference instant (default:DateTime.utc_now/0).:half_life_days— decay half-life (default14).:limit— how many to return (default5).
Examples
iex> now = ~U[2026-07-29 12:00:00Z]
...>
...> facts = [
...> {:old_favourite, ~U[2026-06-29 12:00:00Z], 2},
...> {:used_today, ~U[2026-07-29 09:00:00Z], 1}
...> ]
...>
...> Flicker.RecentValues.rank(facts, now: now)
[:used_today, :old_favourite]
iex> Flicker.RecentValues.rank([], now: ~U[2026-07-29 12:00:00Z])
[]
Records a value as used, through the host's record function.
Fire-and-forget: always returns :ok, and a failing record is logged at
debug rather than surfacing. Recording happens on commit — a chosen
suggestion, a confirmed selection, a cleanly-parsed token on dispatch — never
on hover, focus, keystroke, or a value that failed validation.