feat(event-triage): per-user check interval, overriding the instance one
Nightly Build / build (push) Successful in 5m4s

Event triage is the one system agent whose right cadence depends on who it
runs for: it fires on inbound events, so someone on a dozen mailing lists
has something waiting on nearly every tick while a quiet account has
something waiting almost never. A single instance-wide interval serves one
of them badly, and the observed failure is the first: the agent starts on
practically every pass.

An admin can now set a per-person interval on that user's page (Users ->
the person -> Event triage). Empty means "follow the instance setting",
which stays the state nobody has a row for.

- New registry table `system_agent_user_settings(agent_id, user_id,
  interval_secs)`. A row is an override and its absence is inheritance --
  no sentinel value, no row seeded at user creation, clearing the field
  deletes the row. Registry rather than the user's own file because the
  writer is the admin and a member's database is unreadable unless they
  happen to be logged in; a setting that could only be changed during its
  subject's session would not be a setting. Keyed by agent_id though only
  one agent uses it, so a future agent's schedule is not a schema change.

- `SystemAgent` gains `interval_secs_for(user_id)`, which `is_due` now
  measures against, and `shortest_interval_secs()`. Both default to the
  existing `interval_secs`, so every other agent implements nothing. The
  second is the non-obvious half: `base_tick` sleeps for the shortest
  interval any enabled agent asks for, so without it an override below the
  instance value would be rounded up to it -- an override that works when
  it lengthens and silently does nothing when it shortens.

- `GET/PUT /api/users/{id}/event-triage`, admin-gated, minutes on the
  wire, null to clear. Nothing rides the bus: the scheduler re-reads the
  interval every tick and due-ness is counted from the user's own last
  attempt, so a change lands on the next wake-up with no push.

Both helpers fail open onto the instance value -- an unreadable registry
must not turn into an agent that stops running for someone.

Docs: docs/system-agents.md gains the per-person section and no longer
reads as if the interval were one number for everybody.
This commit is contained in:
Daniele
2026-08-14 13:07:45 +01:00
parent 402c9ffe50
commit e7c802f0d7
13 changed files with 573 additions and 12 deletions
+8
View File
@@ -1099,6 +1099,7 @@ export default {
'users.detail.profile': 'Profil',
'users.detail.connectors': 'Connecteurs',
'users.detail.plugins': 'Plugins',
'users.detail.triage': "Tri des événements",
'users.detail.security': 'Sécurité',
'users.detail.saved': 'Enregistré',
'users.detail.delete_hint': 'Supprime cet utilisateur, sa base de données et tout l\'historique des conversations.',
@@ -1115,6 +1116,13 @@ export default {
'users.plug.empty': 'Aucun plugin disponible.',
'users.plug.admin_note': "Les administrateurs peuvent utiliser tout plugin activé, quelles que soient les cases cochées ici.",
'users.triage.hint': "Le tri des événements lit les événements poussés par les connecteurs de cette personne et lui signale ceux qui méritent une interruption. Quelqu'un qui reçoit beaucoup de courrier ou de messages le déclenche à presque chaque passage : donnez-lui ici une cadence plus lente.",
'users.triage.interval': "Intervalle de vérification (minutes)",
'users.triage.placeholder': "Par défaut ({n})",
'users.triage.using_default': "Vide : suit le réglage de l'instance ({n} min).",
'users.triage.using_override': "Pour cet utilisateur uniquement. Videz le champ pour revenir au réglage de l'instance.",
'users.triage.invalid': "Saisissez un nombre entier de minutes, ou laissez le champ vide.",
'users.modal.create_title': 'Nouvel utilisateur',
'users.modal.username': 'Nom d\'utilisateur',
'users.modal.display_name': 'Nom d\'affichage',