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).
new/2 builds one. duration/2 and window/2 say how long it runs
and where it must fall; needs/3, roster/3 and invite/3 add what
it requires and who may attend, and prefers/2 adds soft preferences.
rosters/1, open_requirements/1 and named_resources/1 read its
requirements back.
In iCalendar
A session is a VEVENT, and what it states lines up with iCalendar
and CalDAV as it is, with nothing renamed:
durationisDURATION.windowis the span planning searches, as CalDAV'stime-rangeis the span a query searches.A roster and an invitation are the two kinds of attendee.
roster/3isATTENDEE;ROLE=REQ-PARTICIPANT: the session cannot be held without them, and they are allocated.invite/3isATTENDEE;ROLE=OPT-PARTICIPANT: they make a time better and are never allocated, because an optional attendee who can cost a placement is not optional.Agenda.Arrangement'sattendinghas no counterpart. It says which invitees a candidate time suits, a planning result rather than a calendar fact; the nearest thing once the session is held isPARTSTAT=ACCEPTED.
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 name.
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 name.
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(), recurrence_id: Tempo.t() | nil, 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.namenames 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 requirements —
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 name.
Arguments
sessionis at/0.nameis the requirement's 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.optionsis a keyword list of options.
Options
:durationis how long the session runs, aTempo.Duration.t/0.:windowis the interval it must fall inside, aTempo.t/0interval.
Returns
- a
t/0.
Examples
iex> import Tempo.Sigils
iex> Agenda.Session.new("Quarterly review", duration: ~o"PT1H").name
"Quarterly review"
@spec open_requirements(t()) :: [Agenda.Requirement.t()]
The requirements described by attribute rather than named.
Each of these is a requirement 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_requirements(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 name.
Arguments
sessionis at/0.nameis the requirement's name, such as:attendees.resourcesis the list of requiredAgenda.Resource.t/0. Naming nobody adds no requirement — a requirement 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 requirement.
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
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-19").window
~o"2026-06-15/2026-06-19"