Cohorts
A cohort is the concrete group that delivers an offer. It keeps that learner group's dates, seats, tutor, and discussion in one place.
The model, without a second membership
Every cohort belongs to exactly one offer. Every enrollment (learner) already points to one offer and can point directly to a cohort of that same offer:
learner.offerIdselects the learner's commercial and delivery terms;learner.cohortIdselects the learner's cohort, when one is needed;learner.cohortRolestores thememberorstarbadge;- there is no separate learner-to-cohort membership.
The enrollment is therefore the only source for rosters, occupied seats, and discussion access. A learner never occupies two cohorts of the same course. Moving them replaces the enrollment's cohortId and removes access to the previous cohort's discussion.
Offer delivery modes
The offer's deliveryMode defines how a cohort participates in delivery:
| Mode | Behavior |
|---|---|
self_paced | The enrollment may exist without a cohort. With discussionEnabled, an open undated cohort can provide the discussion space. |
cohort | Enrollment requires a dated cohort of the same offer. Its start anchors drip for the whole group. |
For self-paced offers, enabling discussion through the standard flow ensures an open General cohort. For cohort-delivered offers, staff creates cohorts or the offer configuration creates them automatically.
Open or closed
| Cohort mode | How the learner enters | Rule |
|---|---|---|
open | The learner chooses it | They need an active enrollment in the same offer and the cohort must accept entry. |
closed | Staff assigns them | Someone with the cohort function — or an owner/admin — can place or move the learner. |
Moving a learner updates the enrollment; it does not create another membership. A cohort with learners or pending requests cannot be deleted until all of them are moved.
Dates, seats, and drip
Offers with deliveryMode: "cohort" require a start and end on every cohort:
| Field | Role |
|---|---|
startsAt | Cohort start and drip anchor. |
endsAt | Cohort end. |
seatLimit | Cohort seats; empty means unlimited. |
enrollClosesAt | Optional cutoff for new enrollments. |
The end must follow the start, the cutoff cannot exceed the end, and a seat limit must be positive. State is derived from the dates:
| State | When | Accepts enrollment? |
|---|---|---|
upcoming | Before startsAt | Yes, while seats and the window are available. |
active | Between start and end | Yes, until the cutoff or end. |
ended | From endsAt onward | No; the cohort remains available as read-only. |
Availability and learner count are checked again under a lock before writing. Two simultaneous enrollments cannot receive the last seat.
Cohort discussion
discussionEnabled belongs to the offer. When enabled, every cohort of that offer has its own conversation. Learners read and post only in the cohort stored on their enrollment; the tutor moderates that cohort, while authorized staff can moderate every cohort of the course.
From endsAt onward, history remains visible to learners who are still assigned to the cohort, but new posts, replies, and author deletions are blocked. Tutor and staff may still pin, resolve, or delete content for moderation. No job changes learner.status: course access remains active, and read-only state is derived directly from the cohort's end date.
The star badge lives in learner.cohortRole. It is visual recognition only and never grants moderation. Overlapping badges render in this order:
- instructor;
- tutor;
- star.
One tutor per cohort
Every cohort has at most one concrete tutor (tutorUserId). This does not limit staff: several people with the cohort function may still manage and moderate the whole course. The field identifies the person specifically accompanying that group.
Eligible tutors are:
- the course owner while they remain in the organization;
- a member of the owning team with the
cohortfunction; - an enrolled learner acting as a peer monitor.
Automatic creation and tutor distribution
A cohort-delivered offer can use:
autoCreateCohortto open a cohort when needed;cohortDurationDaysto calculate its end;defaultCohortSeatLimitfor initial capacity;cohortTutorIdto fix one tutor.
When cohortTutorId is empty, the system keeps the least-loaded distribution: it counts the cohorts already assigned to every eligible candidate and picks the person with the fewest. Ties follow the pool's stable order. There is no multi-tutor table or stored round-robin pointer; the selected tutor is written directly to the cohort.
The pool comes from owning-team members who hold the cohort function and remain in the organization. Without an owning team, the course owner is the fallback. Automatic configuration is refused when no eligible tutor exists.