Monitoring bulk-action runs β
Every bulk action you fire β assign, attendance, delete, send-message, edit β shows up on /bulk-actions as a row in the run history. That page is your inspection point: did the run finish, did rows fail, do you need to retry, and what exactly changed.

The page auto-polls every 5 seconds while any run is active, so you don't need to refresh.
Run states β
Each row has a status badge:
| State | Meaning |
|---|---|
| Queued | The run is in the work queue but the worker hasn't started it yet. Usually milliseconds. |
| Running | Worker is processing rows. The progress bar shows N of M done. |
| Completed | All rows applied with no failures. |
| Partial | Some rows applied, some failed. Most actionable state β you'll usually retry. |
| Failed | A system-level error stopped the run (database unavailable, permission revoked mid-run). |
| Cancelled | You hit Cancel. Already-applied rows stay; pending rows skip. |
Opening a run β
Click any row and you'll see the run detail page:
- Summary cards β total rows, applied, failed, skipped, duration.
- Selection snapshot β the exact list of member IDs the run targeted, captured at click-Apply time.
- Failed items β every row that didn't apply, with a reason.
- Retry failed button (when failures exist).
- Cancel button (when the run is queued or running).
- Re-run button (when the run is finished, regardless of outcome).
The failed-items list β
A row fails when the worker tries to write it and the database rejects the operation. Common reasons:
| Error | What it means | Usually a |
|---|---|---|
permission denied | The current user lost the permission since clicking Apply | Permission was revoked mid-run |
member not found | The member was deleted by someone else during the run | Race condition |
unique violation | The write would create a duplicate | Re-running an old assign on members already in the target |
null value violates not-null | A required field was missing | Almost always a workflow misconfiguration |
The reason text appears inline next to each failed member, so you can scan the list and decide if the run is worth retrying as-is or if you need to fix the underlying problem first.
Retry-failed is idempotent
The retry button re-queues only the rows that failed, with the same parameters. Already-applied rows stay applied β the action doesn't reapply them. Safe to click as many times as you need until either every row succeeds or the failure reason changes (e.g. permission was restored).
Cancel mid-flight β
For long-running actions on thousands of rows, you can Cancel from the run detail page. The worker stops fetching the next batch and the run moves to Cancelled. Already-applied rows stay applied. The selection snapshot is preserved, so you can re-run on the same list later.
Cancel is not undo
Cancelling stops further writes, but it does not roll back what's already been written. If you cancelled a delete and 200 of 500 members were already soft-deleted, those 200 stay soft-deleted. Use the recycle bin to restore them if needed.
How long is the history kept β
Run rows live in bulk_action_runs and stick around for 90 days. After that, a nightly job archives them off the live table. Permanent audit of who-changed-what is in audit log, which retains indefinitely.
Notifications β
If a run takes more than 30 seconds, GCM emails the user who started it when it finishes. If the run hits Partial or Failed, the email includes a link straight to the run detail page. For runs under 30 seconds you'll see the result in-page, no email.
Next steps β
- Bulk assign & attendance β the most common bulk actions.
- Delete & restore β soft-delete and the 30-day window.
- Audit log β long-term record of what changed.
- Users & roles β Audit log β for tracing permission grants and revokes.