09 — User Behaviour (Scheduler Module)
- B1 — Operators scan, they don't search first
- B2 — Status is derived, never guessed
- B3 — Refresh cadence follows the fastest job
- B4 — Reads are cheap; writes are gated
- B5 — Destructive acts require exact confirmation
- B6 — Recovery paths are explicit
- B7 — Offline behaviour
- B8 — Formatting rules
- B9 — Never auto-mutate
- B10 — Analytics (proposed)
Behaviour rules for the operator/admin console — how users read, refresh, act and recover. All console behaviours are
(proposed)except those anchored toscheduler.controller.ts:23-46. Baseline behaviours per 00-shared/04/00-shared/08; conflicts resolve in favour of00-shared.
B1 — Operators scan, they don't search first
The job list is ≤ a few dozen rows (10 defaults scheduler.service.ts:48-119
- custom). Default view = full list grouped by queue, status badges on every row (07 C1). Search only when > 50 rows (not expected).
B2 — Status is derived, never guessed
Today GET /scheduler returns {queue, name, pattern, tz} only
(scheduler.service.ts:152-186) — no status field. Until the (proposed)
run-history endpoint exists, the UI must not fabricate statuses: unknown
rows render the grey unknown badge (C1), and the summary strip shows
"0 known" rather than "all healthy".
B3 — Refresh cadence follows the fastest job
Auto-poll 60 s on S1 (matches audit-flush */1 * * * *,
scheduler.service.ts:78-83 — the highest-frequency default) plus
RefreshIndicator pull. Poll pauses in background and resumes on focus
(shared baseline).
B4 — Reads are cheap; writes are gated
Reads: GET /scheduler only (scheduler.controller.ts:23-28). Writes:
create/remove are superadmin-only (scheduler.read/create/delete,
scheduler.controller.ts:24,31,38) — UI hides actions without permission,
never shows a 403-triggering button.
B5 — Destructive acts require exact confirmation
Remove (F3) and DLQ-delete (F4) always confirm with the identifying triple (name+pattern+queue) or the DLQ record id. No bulk delete, no "apply to all".
B6 — Recovery paths are explicit
Missed run → manual trigger (S5) or DLQ replay (S6). Both warn about
duplication risk (F2/F4) and the UI surfaces the idempotency note
(IdempotencyService, idempotency.service.ts:11-20) so the operator can
decide whether a re-run is safe.
B7 — Offline behaviour
S1/S2/S6 are read-only: last-good payload + AppOfflineBanner; writes
disabled. On reconnect → immediate refresh. Timestamps render "last known".
B8 — Formatting rules
| Value | Rule |
|---|---|
| Cron | monospace as-is (never re-write the 5 fields) |
| Humanized schedule | derived via the C2 map (07 C2) — display only |
| Time | both relative ("2 h ago") and absolute UTC + device-local; never claim a local run time (all jobs run UTC, scheduler.service.ts:48-119) |
| Durations | ms for < 1 s, seconds above; no client math on totals |
| Patterns | 5-field cron, never 6-field (dto regex dto:20-24) |
B9 — Never auto-mutate
No client-side re-enqueue, no optimistic "Run now" without confirm (F2). All state changes go through the API; UI only reflects server truth.
B10 — Analytics (proposed)
{screen}.{action} events per 05 §Analytics; none instrumented today
(00-shared/10 §8).