feat(backend): feed RSVPs into the session at-risk evaluation (#525) #636

Merged
claude-bot merged 1 commit from feat/525-rsvp-at-risk into main 2026-09-14 01:58:24 +00:00
Collaborator

The warning was reading the older of two answers.

A slot vote chose a date, often weeks before the evening. session_attendance.rsvp_status answers "are you coming to this session", usually days before it. evaluate_session_at_risk counted only the former.

The model

Each member contributes 1, 0.5 or 0 to likely_attendance:

  • 0 if absent over the session's time. An absence is a recorded fact about the calendar rather than an opinion about the evening, so nothing below overrides it.
  • otherwise their most recent answer decides: an RSVP wins where there is one (no → 0, maybe → 0.5, yes → 1); with no RSVP the slot vote stands exactly as before.

Two judgement calls, both stated in the code

maybe at half a person — the reading that survives a GM looking at the same screen. Counting it as a yes makes a session where everyone answered "maybe" look perfectly healthy; counting it as a no cries wolf on the tables where "maybe" is just the polite answer.

"Most recent answer wins" is slightly wider than the letter of the issue, which asks only that no and maybe be counted. It is the same sentence's intent — the warning should reflect what people said most recently — and without it a member who voted no weeks ago and has since RSVP'd yes stays counted absent forever, which is the confusing half of the old behaviour rather than a safe part of it. Flagging it because it changes when pings fire in a direction the bullet didn't literally ask for.

A "yes" does not beat a recorded absence. Those two disagreeing is worth a GM's attention, not silent resolution in favour of the cheerier one.

The fractional figure

at_risk is judged on the exact value; the reported likely_attendance is that value floored, because it goes into a Discord message and a %d log line, where "4.5 people" is not something anyone expects to read. Rounding down keeps the number a player sees from ever overstating the figure the warning was decided on — so 3.5 against a quorum of 3 reads "3 likely" and does not warn.

Behaviour change worth knowing

This fires on tables it used to miss (three RSVP "no"s against a unanimous slot vote) and stops nagging about ones where everybody has since said they are coming. That is the point of the issue, but it does move when the Discord ping goes out.

Tests

Six new tests in test_availability.py; four were confirmed to fail against the pre-change source. The other two are guards that pass either way by design: an absence not overridden by a "yes", and a NULL rsvp_status leaving the old slot-vote behaviour alone.

Backend suite: 3472 passed, 13 skipped. ruff check / ruff format --check clean under CI's pinned 0.4.4.

Closes #525

🤖 Generated with Claude Code

The warning was reading the older of two answers. A slot vote chose a *date*, often weeks before the evening. `session_attendance.rsvp_status` answers "are you coming to this session", usually days before it. `evaluate_session_at_risk` counted only the former. ## The model Each member contributes **1, 0.5 or 0** to `likely_attendance`: - **0** if absent over the session's time. An absence is a recorded fact about the calendar rather than an opinion about the evening, so nothing below overrides it. - otherwise their **most recent answer** decides: an RSVP wins where there is one (`no` → 0, `maybe` → 0.5, `yes` → 1); with no RSVP the slot vote stands exactly as before. ## Two judgement calls, both stated in the code **`maybe` at half a person** — the reading that survives a GM looking at the same screen. Counting it as a yes makes a session where *everyone* answered "maybe" look perfectly healthy; counting it as a no cries wolf on the tables where "maybe" is just the polite answer. **"Most recent answer wins" is slightly wider than the letter of the issue**, which asks only that `no` and `maybe` be counted. It is the same sentence's intent — the warning should reflect what people said most recently — and without it a member who voted `no` weeks ago and has since RSVP'd `yes` stays counted absent forever, which is the confusing half of the old behaviour rather than a safe part of it. Flagging it because it changes when pings fire in a direction the bullet didn't literally ask for. **A "yes" does not beat a recorded absence.** Those two disagreeing is worth a GM's attention, not silent resolution in favour of the cheerier one. ## The fractional figure `at_risk` is judged on the exact value; the reported `likely_attendance` is that value **floored**, because it goes into a Discord message and a `%d` log line, where "4.5 people" is not something anyone expects to read. Rounding down keeps the number a player sees from ever overstating the figure the warning was decided on — so 3.5 against a quorum of 3 reads "3 likely" and does *not* warn. ## Behaviour change worth knowing This fires on tables it used to miss (three RSVP "no"s against a unanimous slot vote) and stops nagging about ones where everybody has since said they are coming. That is the point of the issue, but it does move when the Discord ping goes out. ## Tests Six new tests in `test_availability.py`; **four were confirmed to fail against the pre-change source**. The other two are guards that pass either way by design: an absence not overridden by a "yes", and a NULL `rsvp_status` leaving the old slot-vote behaviour alone. Backend suite: `3472 passed, 13 skipped`. `ruff check` / `ruff format --check` clean under CI's pinned 0.4.4. Closes #525 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(backend): feed RSVPs into the session at-risk evaluation (#525)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 30s
CI / Docker image build (pull_request) Successful in 38s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 56s
CI / Frontend tests, audit, and build (pull_request) Successful in 2m48s
CI / Bot/backend version sync (pull_request) Successful in 1m11s
CI / Bot tests and audit (pull_request) Successful in 1m48s
CI / Backend migration, tests, and audit (pull_request) Successful in 15m51s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 15m14s
a560c4235b
`evaluate_session_at_risk` counted explicit time-slot votes only. A slot vote
chose a *date*, often weeks before the evening; `session_attendance.rsvp_status`
answers "are you coming to this session", usually days before it. The warning
was reading the older of the two answers.

Each member now contributes 1, 0.5 or 0 to `likely_attendance`:

- 0 if they are absent over the session's time. An absence is a recorded fact
  about the calendar rather than an opinion about the evening, so nothing below
  overrides it — a "yes" against a recorded absence is a disagreement worth a
  GM's attention, not something to resolve silently in favour of the cheerier
  answer.
- otherwise their most recent answer decides: an RSVP wins where there is one
  (`no` 0, `maybe` 0.5, `yes` 1), and with no RSVP the slot vote stands exactly
  as before.

"Most recent answer wins" is slightly wider than the letter of #525, which asks
only that `no` and `maybe` be counted. It is the same sentence's intent, and
without it a member who voted `no` weeks ago and has since RSVP'd `yes` would
be counted absent forever — the confusing half of the old behaviour rather
than a safe part of it.

`maybe` at half a person is the reading that survives a GM looking at the same
screen: counting it as a yes makes a session where everyone answered "maybe"
look perfectly healthy, and counting it as a no cries wolf on tables where
"maybe" is simply the polite answer.

`at_risk` is judged on the exact fractional figure; the reported
`likely_attendance` is that figure floored, because it is rendered to players
in a Discord message and a `%d` log line where "4.5 people" is not something
anyone expects to read. Rounding down keeps the number a GM sees from ever
overstating the figure the warning was decided on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/525-rsvp-at-risk 2026-09-14 01:58:25 +00:00
Sign in to join this conversation.
No description provided.