-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat(signals): add scout lifecycle status with reason-scoped pauses #75349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d1f80c4
e250efc
1f8a331
b4a977f
3f20495
86f7016
7cd3a74
dcbba0d
6f44179
36ae53d
c7dd794
dbda4ab
ec822dd
2949a69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import django.db.models.deletion | ||
| from django.conf import settings | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
| dependencies = [ | ||
| ("signals", "0076_signalscoutnote_discussion_origin"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name="signalscoutconfig", | ||
| name="status", | ||
| field=models.CharField( | ||
| choices=[ | ||
| ("active", "Active"), | ||
| ("pending_pause", "Pending pause"), | ||
| ("paused_by_system", "Paused by system"), | ||
| ("paused_by_user", "Paused by user"), | ||
| ], | ||
| db_default="active", | ||
| default="active", | ||
| max_length=20, | ||
| ), | ||
| ), | ||
| migrations.AddField( | ||
| model_name="signalscoutconfig", | ||
| name="pause_reason", | ||
| field=models.CharField( | ||
| blank=True, | ||
| choices=[ | ||
| ("no_output", "No output"), | ||
| ("ignored", "Ignored"), | ||
| ("repeated_failures", "Repeated failures"), | ||
| ], | ||
| max_length=20, | ||
| null=True, | ||
| ), | ||
| ), | ||
| migrations.AddField( | ||
| model_name="signalscoutconfig", | ||
| name="status_changed_at", | ||
| field=models.DateTimeField(blank=True, null=True), | ||
| ), | ||
| migrations.AddField( | ||
| model_name="signalscoutconfig", | ||
| name="status_changed_by", | ||
| field=models.ForeignKey( | ||
| blank=True, | ||
| db_constraint=False, | ||
|
Comment on lines
+49
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Although Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in f33bc52: |
||
| db_index=False, | ||
| null=True, | ||
| on_delete=django.db.models.deletion.SET_NULL, | ||
| related_name="+", | ||
| to=settings.AUTH_USER_MODEL, | ||
| ), | ||
| ), | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| from django.db import migrations | ||
|
|
||
|
|
||
| def backfill_paused_by_user(apps, schema_editor): | ||
| # Rows disabled before `status` existed can only have been switched off by a human: | ||
| # nothing system-driven ever wrote `enabled` until this field shipped. One UPDATE over a | ||
| # small table (scout configs, not events); kept separate from 0077's AddFields so the | ||
| # data write never shares a transaction with schema locks. | ||
| SignalScoutConfig = apps.get_model("signals", "SignalScoutConfig") | ||
| # `_default_manager`, not `objects`: the model's Meta routes the default manager to the | ||
| # unscoped `all_teams`, so the historical model carries no `objects` attribute. | ||
| SignalScoutConfig._default_manager.filter(enabled=False).update(status="paused_by_user") | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
| dependencies = [ | ||
| ("signals", "0077_signalscoutconfig_status"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.RunPython(backfill_paused_by_user, reverse_code=migrations.RunPython.noop), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 0076_signalscoutnote_discussion_origin | ||
| 0078_backfill_scout_status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When staff use the retained
enabledcheckbox to re-enable a paused scout, the default admin save reaches the model's actorless reconciliation path, which clearsstatus_changed_by;in_cold_start_grace()therefore does not grant the documented grace period for this human re-enable. Similarly, editing an active field such as the schedule oremitwhile the scout ispending_pausedoes not clear that warning as the API human-write path does. Add lifecycle-aware admin save handling that stampsrequest.userand applies the same transition semantics as the update serializer.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considered, leaving as is. Admin is a break-glass surface, not the product write path, and both divergences fail in the conservative direction: no grace re-anchor means a staff-touched scout gets evaluated sooner (not pause-exempt longer), and an uncleaned
pending_pausemeans the warning stays visible rather than being silently dismissed. Duplicating the serializer's human-write semantics insave_model(or weakening the rule that the actorless reconciliation clears attribution) buys little for the risk; if staff lifecycle operations become a real flow they should go through the API, which carries the full semantics.