A session — the thing being scheduled.
A session states how long it runs, the window it must fall inside, what it requires of resources, and what it would merely prefer. It says nothing about when it actually happens: that is what planning works out.
The word is deliberately not "meeting" (too narrow — a court hearing
and a conference talk are neither), not "event" (Tempo.Event is
taken, and it collides with event sourcing), and not "booking" (which
names the request in some scheduling libraries, not the thing
requested).
Summary
Functions
Set how long the session runs.
Invite resources who may attend but are not required.
Every resource named by a roster requirement.
Add an attribute requirement under role.
Build a session.
The requirements described by attribute rather than named.
Add soft preferences — they rank arrangements but never exclude one.
Add a requirement naming specific resources under role.
The requirements naming specific resources.
Set the interval the session must fall inside.
Types
@type t() :: %Agenda.Session{ duration: Agenda.Availability.pattern() | nil, invitees: keyword([Agenda.Resource.t()]), name: String.t(), preferences: keyword(), requirements: [Agenda.Requirement.t()], series: String.t() | nil, window: Agenda.Availability.pattern() | nil }
Something to be scheduled.
Functions
@spec duration(t(), Agenda.Availability.pattern()) :: t()
Set how long the session runs.
Arguments
sessionis at/0.durationis aTempo.Duration.t/0.
Returns
- the session, with its duration set.
Examples
iex> import Tempo.Sigils
iex> session = Agenda.Session.new("Review")
iex> Agenda.Session.duration(session, ~o"PT1H").duration
~o"PT1H"
@spec invite(t(), atom(), [Agenda.Resource.t()]) :: t()
Invite resources who may attend but are not required.
The counterpart to roster/3. A rostered resource must be free or
the session cannot be held; an invitee never affects whether a
placement is possible, only how good it is — a time when more of
them can come scores better, and Agenda.Arrangement records which
of them the chosen time actually suits.
This is deliberately weaker than roster/3 in a second way: an
invitee is not allocated. Their time is not taken and the ledger
does not know about them, because a placement that consumed an
optional person could make some other session impossible — and an
optional attendee that can cost a placement is not optional. Book
them with Agenda.Ledger.allocate/3 once the time is settled, if
they are coming.
Arguments
sessionis at/0.rolenames what the invitees would be there as, the same wayroster/3does.resourcesis a list ofAgenda.Resource.t/0.
Returns
- The session, with the invitees added.
Examples
iex> bob = Agenda.resource("Bob")
iex> session = Agenda.session("Review", duration: "PT1H")
iex> Agenda.Session.invite(session, :optional, [bob]).invitees
[optional: [bob]]
@spec named_resources(t()) :: [Agenda.Resource.t()]
Every resource named by a roster requirement.
These are the resources whose own requires tighten the open roles —
see Agenda.Requirement.induce/2.
Arguments
sessionis at/0.
Returns
- the named resources, in requirement order.
Examples
iex> alice = Agenda.Resource.new("Alice")
iex> session = Agenda.Session.new("Review") |> Agenda.Session.roster(:attendees, [alice])
iex> Enum.map(Agenda.Session.named_resources(session), & &1.name)
["Alice"]
Add an attribute requirement under role.
Arguments
sessionis at/0.roleis the role name, such as:room.predicatesis a keyword list of attribute predicates.
Returns
- the session with the requirement added.
Examples
iex> import Agenda.Predicate
iex> session = Agenda.Session.new("Review")
iex> session = Agenda.Session.needs(session, :room, seats: at_least(8))
iex> Enum.map(session.requirements, & &1.name)
[:room]
Build a session.
Arguments
nameis the session's name.
Options
:durationis how long the session runs, aTempo.Duration.t/0.:windowis the interval it must fall inside, aTempo.Interval.t/0.
Returns
- a
t/0.
Examples
iex> import Tempo.Sigils
iex> Agenda.Session.new("Quarterly review", duration: ~o"PT1H").name
"Quarterly review"
@spec open_roles(t()) :: [Agenda.Requirement.t()]
The requirements described by attribute rather than named.
Each of these is a role that planning must choose a resource for.
Arguments
sessionis at/0.
Returns
- the attribute requirements.
Examples
iex> session = Agenda.Session.new("Review") |> Agenda.Session.needs(:room, seats: 8)
iex> Enum.map(Agenda.Session.open_roles(session), & &1.name)
[:room]
Add soft preferences — they rank arrangements but never exclude one.
Arguments
sessionis at/0.preferencesis a keyword list.within: placeprefers resources inside that place; any other key is matched against resource attributes.
Returns
- the session, with the preferences added.
Examples
iex> sydney = Agenda.Place.new("Sydney")
iex> session = Agenda.Session.new("Review")
iex> Agenda.Session.prefers(session, within: sydney).preferences |> Keyword.keys()
[:within]
@spec roster(t(), atom(), [Agenda.Resource.t()]) :: t()
Add a requirement naming specific resources under role.
Arguments
sessionis at/0.roleis the role name, such as:attendees.resourcesis the list of requiredAgenda.Resource.t/0. Naming nobody adds no requirement — a role filled by no one constrains nothing, and a requirement that neither names nor describes would otherwise let planning bind any resource at all to the role.
Returns
- the session with the requirement added, or unchanged if
resourcesis empty.
Examples
iex> alice = Agenda.Resource.new("Alice")
iex> session = Agenda.Session.new("Review")
iex> session = Agenda.Session.roster(session, :attendees, [alice])
iex> Enum.map(session.requirements, & &1.name)
[:attendees]
iex> session = Agenda.Session.new("Review")
iex> Agenda.Session.roster(session, :attendees, []).requirements
[]
@spec rosters(t()) :: [Agenda.Requirement.t()]
The requirements naming specific resources.
Arguments
sessionis at/0.
Returns
- the roster requirements.
Examples
iex> alice = Agenda.Resource.new("Alice")
iex> session = Agenda.Session.new("Review") |> Agenda.Session.roster(:attendees, [alice])
iex> Enum.map(Agenda.Session.rosters(session), & &1.name)
[:attendees]
@spec window(t(), Agenda.Availability.pattern()) :: t()
Set the interval the session must fall inside.
Arguments
sessionis at/0.windowis aTempo.Interval.t/0.
Returns
- the session, bounded.
Examples
iex> import Tempo.Sigils
iex> session = Agenda.Session.new("Review")
iex> Agenda.Session.window(session, ~o"2026-06-15/2026-06-20").window
~o"2026-06-15/2026-06-20"