recurring-reports-post-offboarding

Installation
SKILL.md

Recurring Reports Firing After Trial Expiry or Workspace Offboarding

This skill diagnoses the class of incidents where users continue receiving report emails after their trial has expired or their workspace has been churned — and provides the mitigation to stop them immediately.

What This Skill Debugs

Churned or expired workspaces can leak emails through two independent email systems. Both must be checked.

System 1 — RecurringReportSchedule → SCHEDULED_GENERATED_CONTRACT_SEARCH_REPORT

RecurringReportSchedule rows are not deleted or deactivated when:

  1. A trial workspace expires — UpdateExpiredTrialSignupsUseCase and PurgeTrialDataUseCase (pre-fix) did not clean up RecurringReportSchedule rows.
  2. A workspace is churned/offboarded without the delete_workspace management command — the soft-delete chain in SoftDeletionModel does not fire Django's DB-level CASCADE, leaving RecurringReportSchedule rows alive.

The daily cron (send_due_today_recurring_report_cron_task) has no trial-status or workspace-active guard — it picks up all RecurringReportSchedule rows where next_send_at falls today and is_deleted=False, regardless of workspace state.

System 2 — EmailConfig + EXPIRING_CONTRACTS_EMAIL_REPORT_ENABLED FF → CONTRACT_EXPIRATION_REPORT

The weekly contract expiry reminder system is independent of RecurringReportSchedule. It fires based on emails_emailconfig rows and the workspace-level feature flag EXPIRING_CONTRACTS_EMAIL_REPORT_ENABLED. When a workspace is churned without running delete_workspace, EmailConfig rows survive and the feature flag remains enabled — so expiry reminder emails keep firing weekly even after all RecurringReportSchedule rows have been soft-deleted.

Installs
1
First Seen
Apr 9, 2026