Scope
Book sessions against mentor availability, with calendar-attachment emails.
Data model
public sealed class Session
{
public Guid Id { get; set; }
public Guid MentorshipId { get; set; }
public DateTimeOffset StartUtc { get; set; } // stored UTC, always
public int DurationMinutes { get; set; } // 30 | 60
public SessionStatus Status { get; set; } // Scheduled | Completed | Cancelled
public string? MeetingUrl { get; set; } // mentor-provided free text (Meet/Zoom)
}
Endpoints (participants of the mentorship only)
| Method |
Path |
Notes |
| GET |
/mentorship/{id}/slots?weekOf= |
open slots: availability minus existing sessions, computed in the mentor's IANA tz via NodaTime, returned as UTC instants |
| POST |
/mentorship/{id}/sessions |
book; validations below |
| POST |
/mentorship/sessions/{id}/cancel |
either participant, only while Scheduled |
Booking validations
- Slot falls entirely inside the mentor's availability (evaluated in mentor-local time — DST-correct via NodaTime).
- No overlap with any of the mentor's existing
Scheduled sessions (across all their mentorships).
- Start ≥ 12h in the future.
- Duration ∈ {30, 60}; mentorship is
Active.
Emails
Booked/cancelled → both parties via Brevo, with an .ics attachment generated via Ical.Net (UTC times, summary, meeting URL when present). Best-effort as always.
Error codes
mentorship.not_found, mentorship.slot_unavailable, mentorship.slot_conflict, mentorship.too_soon, mentorship.invalid_state, mentorship.forbidden
Dependencies
Blocked by #9 and #10.
Acceptance criteria
Conventions (project-wide, non-negotiable)
- .NET 9, records for immutable shapes, file-scoped namespaces, primary constructors where they read well. Minimal-API endpoints grouped per module via
IEndpointModule.MapEndpoints.
Result<T> (SharedKernel) instead of exception-driven control flow. Endpoint results map failures to ProblemDetails with the stable error codes listed above — the frontend keys off them.
- Module owns its EF Core
DbContext mapped to its own Postgres schema. Modules never reference each other's internals — cross-module needs go through a public contract interface or an in-process domain event (IEventPublisher).
- All external calls (GitHub, Stripe, Brevo, Cloudinary, Meilisearch) behind interfaces owned by the consuming module.
- Every list endpoint paginated (offset is fine). xUnit tests in
tests/CommunityPro.Tests/<Module>/ following the existing harness patterns (see Members/MembersTestHarness.cs).
Scope
Book sessions against mentor availability, with calendar-attachment emails.
Data model
Endpoints (participants of the mentorship only)
/mentorship/{id}/slots?weekOf=/mentorship/{id}/sessions/mentorship/sessions/{id}/cancelScheduledBooking validations
Scheduledsessions (across all their mentorships).Active.Emails
Booked/cancelled → both parties via Brevo, with an
.icsattachment generated via Ical.Net (UTC times, summary, meeting URL when present). Best-effort as always.Error codes
mentorship.not_found,mentorship.slot_unavailable,mentorship.slot_conflict,mentorship.too_soon,mentorship.invalid_state,mentorship.forbiddenDependencies
Blocked by #9 and #10.
Acceptance criteria
Europe/LondonorAmerica/New_Yorkbooks correctly; test both spring-forward and fall-back weeksConventions (project-wide, non-negotiable)
IEndpointModule.MapEndpoints.Result<T>(SharedKernel) instead of exception-driven control flow. Endpoint results map failures to ProblemDetails with the stable error codes listed above — the frontend keys off them.DbContextmapped to its own Postgres schema. Modules never reference each other's internals — cross-module needs go through a public contract interface or an in-process domain event (IEventPublisher).tests/CommunityPro.Tests/<Module>/following the existing harness patterns (seeMembers/MembersTestHarness.cs).