The changes that shipped, before they become entries: what the app's
sources bring (Tayori.Source), what its CI, GitHub and its own code
add, what an agent adds over MCP. The admin reads them in the inbox,
puts aside what readers need not know, and a digest makes an entry of
the rest (Tayori.Digest).
A change is the source's and its ref's: adding it again changes nothing, so a source can give all it has each time it is read.
Tayori.Inbox.add(%{ref: "pr-1234", title: "Exports in two clicks", kind: "added"},
source: "app"
)A change that does not say its audience takes the :inbox config's
("public"); one that does not say its category takes its kind's
(the config's :kinds, or the category whose key is the kind).
Summary
Functions
Adds a change, or finds the one with the same source and ref. Options:
:source ("app"). Returns {:ok, change}, or {:error, changeset}.
Adds changes, each as add/2 would. Returns how many were new, how
many were there already, and the errors of those that could not be
added, {index, changeset}.
How many changes are in each state.
A change by its id, or nil.
Puts a change aside: no digest takes it.
Reads every source of the :inbox config, and adds their changes.
Returns each source's result, by its name: {:ok, counts} (as
add_many/2), or {:error, reason}, logged. With the :digest
config's every: :ingest, new changes start a digest
(Tayori.Digest.after_ingest/1).
The changes, the newest first. Options: :state ("new", "drafted",
"hidden"), :source, :audience, :entry_id, :ids, :search
(words of the title), :limit (100).
The changes waiting for a digest: new, and for the public. Options:
:ids (only these), :since and :until (when they happened).
How many changes are waiting for a digest: new, and for the public.
Brings back a change put aside, or one drafted whose entry is gone.
Changes what a change says: its title, body, category, audience, segments, highlight.
Functions
@spec add(map(), keyword()) :: {:ok, Tayori.Change.t()} | {:error, Ecto.Changeset.t()}
Adds a change, or finds the one with the same source and ref. Options:
:source ("app"). Returns {:ok, change}, or {:error, changeset}.
@spec add_many([map()], keyword()) :: %{ added: non_neg_integer(), existing: non_neg_integer(), errors: [{non_neg_integer(), Ecto.Changeset.t()}] }
Adds changes, each as add/2 would. Returns how many were new, how
many were there already, and the errors of those that could not be
added, {index, changeset}.
@spec counts() :: %{required(String.t()) => non_neg_integer()}
How many changes are in each state.
@spec get(Ecto.UUID.t()) :: Tayori.Change.t() | nil
A change by its id, or nil.
@spec hide(Tayori.Change.t()) :: {:ok, Tayori.Change.t()}
Puts a change aside: no digest takes it.
Reads every source of the :inbox config, and adds their changes.
Returns each source's result, by its name: {:ok, counts} (as
add_many/2), or {:error, reason}, logged. With the :digest
config's every: :ingest, new changes start a digest
(Tayori.Digest.after_ingest/1).
@spec list(keyword()) :: [Tayori.Change.t()]
The changes, the newest first. Options: :state ("new", "drafted",
"hidden"), :source, :audience, :entry_id, :ids, :search
(words of the title), :limit (100).
@spec pending(keyword()) :: [Tayori.Change.t()]
The changes waiting for a digest: new, and for the public. Options:
:ids (only these), :since and :until (when they happened).
@spec pending_count() :: non_neg_integer()
How many changes are waiting for a digest: new, and for the public.
@spec restore(Tayori.Change.t()) :: {:ok, Tayori.Change.t()}
Brings back a change put aside, or one drafted whose entry is gone.
@spec update(Tayori.Change.t(), map()) :: {:ok, Tayori.Change.t()} | {:error, Ecto.Changeset.t()}
Changes what a change says: its title, body, category, audience, segments, highlight.