> For the complete documentation index, see [llms.txt](https://help.resouris.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.resouris.com/how-it-works/sickness-and-leave.md).

# Sickness and leave

Two orthogonal disruption paths, both handled without a full roster regeneration. Both flow through the reschedule-proposal mechanism so admins retain final control.

## Sickness

Two paths reach the sickness subsystem:

### Worker self-report

A worker signs into `/ui/my-roster`, clicks **Report sick**, and picks a date range. The system:

1. Looks up any **open** sickness batch matching the worker's `department_id` and overlapping the date range.
2. If a match exists, joins the worker to that batch (F4 predicate from P23).
3. If no match, creates a new batch in `open` state with `K=3` (stub for programmatic clients).
4. Emits `sickness.self_reported` audit event.

The batch is what triggers the reschedule worker. The individual self-report is one row inside the batch.

### Admin-authored batch (the wizard)

Start a batch at `/ui/admin/sickness-batches/new`; the dashboard of open batches is `/ui/admin/sickness-batches-dashboard`. The wizard walks:

1. Pick affected staff (one or many).
2. Pick date range.
3. Pick affected roster area.
4. Optionally add notes.
5. Submit → batch enters `open` state.

Batches, not individual sickness rows, are the unit of triage. When five staff call in sick on the same morning during flu season, the wizard groups them so the reschedule proposals see all five constraints together rather than triaging them one-at-a-time (which produces worse cover).

### Sibling reschedule proposals

The reschedule worker consumes the batch and produces candidate proposals per affected sub-window. For a single absent slot, multiple candidate covers are generated as **sibling proposals**. Admin reviews `/ui/admin/reschedule-proposals` and accepts one — the acceptance atomically transitions the sibling proposals to `superseded_by_sibling` (per MIG-140).

Sickness batches close when all affected sub-windows are covered or acknowledged as uncovered. State machine: `open → resolving → closed`.

### The 30-day banner and the audit chain

If your air-gapped deployment falls behind on revocation-list imports for 30+ days, the sickness-report flow still works — but a yellow banner appears on the admin nav noting the revocation list is stale. This is a soft signal; it does not block operations.

## Leave

Leave requests are a proper state machine, not an ad-hoc email chain.

### Submit (worker)

A worker requests leave at `/ui/my-roster/leave/new`. Fills:

* Date range.
* Reason (categorised: annual, study, sickness-followup, compassionate, unpaid, other).
* Notes.
* Contact-if-urgent flag.

Submits → row enters `leave_requests` in state `pending`. Emits `leave.request.submitted`. Worker receives a confirmation email.

### Decide (admin)

Decisions are made on the leave dashboard, `/ui/admin/leave-dashboard`. Filter tabs: `pending | approved | denied | withdrawn`. Per-area or tenant-wide roll-up.

Options per request:

* **Approve** — request state → `approved`. Emits `leave.request.decided` with outcome `approved`. Affected sub-slots are marked for the reschedule worker.
* **Deny** — request state → `denied`. Worker receives notification with the admin-authored reason.
* **Skip pending** — admin-on-behalf shortcut: goes straight to `approved` from any state. Leaves `submitted_by_user_id` NULL to distinguish operator-authored rows.

Batch approve / deny action available on the dashboard for multi-select.

### Withdraw (worker)

Worker can withdraw any request in state `pending`. Row transitions to `withdrawn` — F5 lock preserves the audit trail rather than hard-deleting. Emits `leave.request.withdrawn`.

Requests already `approved` cannot be withdrawn by the worker; admin has to `decide` them back down. This preserves the rescheduling that was already performed.

### The five-value state machine

```
pending → approved | denied | withdrawn
approved (by admin) → withdrawn (by admin acting on-behalf)
```

`withdrawn` is a first-class state, not a soft-delete. The row remains queryable for compliance.

## Cross-cutting: admin dashboards

Two dashboards, both introduced in P24:

* **`/ui/admin/leave-dashboard`** — per-area + tenant-wide roll-up of leave requests. Filter by state, area, staff. Batch actions. Gated on `admin.leave-requests.dashboard.read`.
* **`/ui/admin/sickness-batches-dashboard`** — per-area + tenant-wide roll-up of sickness batches. Click-through to candidates. Gated on `admin.sickness-batches.dashboard.read`.

Unread-decisions badge on the admin nav computed via `WHERE decided_utc > last_seen_utc`.

## The MyReSouris notify-back badge

Workers see a small nav badge counting decisions they haven't seen yet — same query, viewer's side. Clicking the badge marks all as seen and clears the count.

## Notification adapters

Both flows emit notifications via the standard notification-dispatch pipeline:

* email (via the tenant's configured SMTP or transactional-email provider),
* Web Push (VAPID + AES128GCM payload encryption),
* optional webhook (for tenants integrating with third-party HR systems).

Notification subscriptions are per-user, per-channel; opt-out at `/ui/my-details → Notifications`.

## Composability

Sickness and leave interact with everything upstream:

* **Roster lifecycle** — sickness / leave triggers reschedule proposals; the published roster stays largely intact.
* **Equity model** — approved leave is excluded from the equity denominator (via the allocation policy's exclusions list). Sickness is treated similarly.
* **Job plans** — sickness / leave do not reduce entitlement (target counts don't change), but the credited-count column does not accumulate during absence.

The P27 split-shifts feature explicitly composes: a P23 sickness batch can enumerate `slot_assignment` rows those staff hold on the absence date via the `find_sub_windows_for_sickness_batch` helper (F9 composability pin).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.resouris.com/how-it-works/sickness-and-leave.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
