From c432edbae2d6f09d5fb493ce5a9ac589a0fbc1b9 Mon Sep 17 00:00:00 2001 From: Inferencebot Date: Wed, 4 Nov 2026 05:20:45 +0000 Subject: [PATCH] membership: record the allowance renewal and lapse policy (2026-11-04) Renewal asks Open Collective for a live recurring contribution on the sweep's schedule, anchored to the member's join day rather than the payment date (OC aligns charges onto a common billing day). One reminder, then a grace window, then deactivate by removing group access while keeping the account. Member-facing summary: co-op/support#44. --- README.md | 1 + membership-allowances.md | 54 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 membership-allowances.md diff --git a/README.md b/README.md index 47775d7..b971828 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Read it on the web here: | Path | Contents | |---|---| | `operator-token-runbook.md` | The Cloudron token model (agent vs portal, admin vs webhook), how to rotate each without breaking the stack | +| `membership-allowances.md` | How a member's monthly allowance renews, how a lapse is handled, and the join-day anchoring rule | | `maintenance/chat-task-models.md` | The small pinned model used for chat titles/tags/suggestions | | `maintenance/librechat-sandbox.md` | Operating the LibreChat evaluation instance (config, overlay image, key sync, pitfalls) | | `incidents/` | Post-incident write-ups, one file per incident | diff --git a/membership-allowances.md b/membership-allowances.md new file mode 100644 index 0000000..c2f9b9f --- /dev/null +++ b/membership-allowances.md @@ -0,0 +1,54 @@ +# Membership allowance renewal and lapse + +Operator-facing record of the decision taken **2026-11-04** on how a member's +monthly allowance renews, and how a lapse is handled. The member-facing summary +is `co-op/support#44`; the code lives in `co-op/member-portal`. + +## The rule + +- **Renewal is decided by asking Open Collective, on our own schedule.** A + scheduled reconciliation asks whether the member has a *live recurring + contribution* (order status `ACTIVE` or `PROCESSING` — a payment in flight is + not a missed payment). Renewal is a decision of the sweep, not a side effect of + webhook delivery, so a lost webhook costs a delayed renewal, never a silently + skipped month. +- **Anchor to the member's join day, never the payment date.** Open Collective + aligns a whole collective's charges onto one common billing day, so anchoring + to a charge lets an early charge move the boundary and skip a cycle. The + member's join day is stored (`members.anchor_day`) and the reset is pinned to + its next occurrence. +- **Grace before deactivation.** One reminder email per cycle; deactivate only + after `PAYMENT_GRACE_DAYS` unpaid. +- **Deactivate by removing group access and stopping renewal, keeping the + account**, so a returning payer resumes cleanly at the next cycle. +- **Log every decision** (`allowance_log`) with the Open Collective evidence — + renewed / reminded / deactivated, plus the payment date or its absence — so a + dispute is answered from a record, not reconstructed. + +## Scope: monthly only + +Verified **2026-11-04** that there are **no annual contributors**: 104 orders on +Open Collective, 87 MONTHLY and 17 ONETIME, zero annual, and no order has a +charge gap over 45 days. Only the monthly case is implemented. One-time +contributions are not an allowance cycle — they top up the non-expiring credit +balance and stay in the existing credits model. + +## Operating it + +- `ALLOWANCE_POLICY` = `report` (default) | `on`. `report` computes every + decision and changes nothing; `on` performs renewals and sends the reminder. +- `ALLOWANCE_DEACTIVATE` = `off` (default) | `on`. Gated separately so renewals + and reminders can run before anyone loses access. +- `PAYMENT_GRACE_DAYS` (default `7`). +- Manual check: `POST /admin/allowance?mode=report` (`X-Admin-Token`) is a no-op + report; `mode=test&to=` emails one sample reminder; `mode=send` requires + `ALLOWANCE_POLICY=on`. + +## The bug that prompted it + +A member who joined on the 15th was charged on the 2nd (Open Collective's common +billing day). The reset rule re-anchored his allowance to the new payment date +and pushed his renewal from the 14th to the 2nd of the next month — skipping the +reset he was owed while he had already paid. The fix is both changes above: +anchor to the join day, and decide renewal on a schedule rather than on the +payment.