> 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/equity-model.md).

# Equity and fairness model

The fairness accounting subsystem — internally called BPP-EQUITY — is the answer to "why did this specific staff member get this specific slot?" It replaces the invisible spreadsheet reasoning most rostering teams do by hand with an explicit, auditable model.

## The problem it solves

Manual rostering distributes work by memory: "you did the on-call last week; I'll try to give you a quieter shift this week." The reasoning is real but invisible. Nobody can produce a defensible answer when a staff member asks "why did I get three weekend shifts in a row?"

ReSouris's answer is an explicit decision order that the CP-SAT solver applies to every candidate assignment, with the inputs defined by tenant-configurable policy. Every scoring decision is stored in `assignment_score_breakdown` and linked to the audit chain.

## The decision order

> ⛔ **THE WORK COMES FIRST, AND FAIRNESS CANNOT OUTBID IT.** This is the single most important thing to understand about how a ReSouris roster is decided, and it is a deliberate ruling: *sessions must be filled if we have the staff, the space and the equipment to do so. They are the primary consideration. Fairness is secondary.*

The solver does not add everything up and pick the biggest number. It solves **four tiers in order**. Each tier is optimised in turn and then **frozen as a constraint** before the next one is considered:

| Tier                   | What it decides                                           | Ordered within the tier by                                                                                                                                                                                                                   |
| ---------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1 — The work**       | Which sessions run, and who is in them.                   | Staffing a mandatory position, then avoiding sending anyone between sites, then honouring a pinned person, then feeding a recertification, then running an ordinary session, then who the better match is, then the service line's priority. |
| **2 — Fairness**       | Level the load across the people who were available.      | First that every placeable person gets *some* work, then levelling the amount between them.                                                                                                                                                  |
| **3 — Preferences**    | Among equally fair answers, honour what people asked for. | A stated preference, then week-over-week continuity of service line.                                                                                                                                                                         |
| **4 — Soft penalties** | The last tie-breaks.                                      | "Prefers not to work with", day-off rotation, and staying close to the last published rota.                                                                                                                                                  |

> ⚠️ **A WEIGHT COMPETES ONLY WITH THE OTHERS IN ITS OWN TIER.** Magnitude orders *within* a tier and means nothing *across* tiers. So raising a preference weight changes a tie-break, not the policy — it cannot buy an empty clinic, because tier 1's value is already frozen by the time preferences are considered. If you report a weight change to a department, say which tier it is in: that is what determines whether it can change anything they will notice.

This replaced a single weighted sum, where a large enough preference weight genuinely could leave a clinic unstaffed. The tiering makes that arithmetically impossible rather than merely unlikely.

## What the solver weighs up about a person

Within tier 1 (who fills a running session) and tier 2 (levelling the load), these are the inputs:

1. **Mandatory requirements** — a named training assignment, or a supervisor on a supervised session. Effectively locks the assignment in.
2. **Competency match** — the tier of match between a person's competencies and what the slot requires. Proficient beats direct-supervised, which beats indirect-supervised.
3. **Equity deficit** — how far below their equitable share this person is on this dimension over the current window. A higher deficit raises their claim on the slot.
4. **Entitlement debt** — how much of this person's job-plan entitlement for this activity is still unfulfilled this period.
5. **Service-line priority** — used as a tie-break within tier 1, below the engine's view of who the better match is.

## Allocation policy

An `allocation_policy` row carries:

* **Weights** — the multipliers for the inputs above, each acting within its own tier.
* **Windows** — the time windows over which equity is measured for each equity dimension (typically 4-weekly, 13-weekly, or 12-monthly).
* **Exclusions** — assignments or dimensions to exclude from the equity computation (e.g. exclude study leave from the on-call ratio).

Policies have a state machine: `draft → active → superseded`. At most one active policy per tenant.

The friendly editor (v1.6 P26.B1) surfaces this as sliders + preset picker rather than raw JSON. Presets: Balanced, Skill-first, Fairness-first, Priority-first, Clone-from-active. Every preset is defensible; adjust from there.

## Equity dimensions

An `equity_dimension` is a way of measuring "fair share" — `weekend_shifts`, `on_call_shifts`, `evening_ooh_shifts`, `night_shifts`, `training_opportunity`, `cpd_time_allocated`, `procedure_exposure`. Seven come with the platform; tenants can extend the catalogue, and the reference estate adds four of its own.

Each dimension has:

* a `computation_kind` from a closed set of six: `slot_assignment_count`, `leave_grant_count`, `procedure_count`, `training_slot_count`, `competency_advancement_count`, `cpd_hours_count`,
* a period (typically 13-weekly),
* a partitioning (usually per-tenant, per-department, or per-role-band).

The `equity_counter` materialised view rolls per-staff totals for every (dimension × period) pair. The solver reads it during scoring.

## Job plans

A `job_plan` is a per-staff contract for activity-level entitlements: "12 CT-Abdomen sessions per 13-week period, 6 MRI sessions, 2 breast-screening clinics." Each entitlement has a target `count`, a period, and optional preference hints.

States: `draft → active → paused → superseded → withdrawn`. Job plans are dated and versioned — you keep the ancestry when you supersede.

The `job_plan_ledger` is the append-only credit log. Every published assignment that matches a plan entitlement emits a credited row. Manual credits, debits, carry-overs, and corrections all live in the same ledger with a `source_kind` closed set (5 values).

The reactive `job_plan_debt` view computes `expected_count = target × periods_elapsed`, `credited_count = SUM(verified_ledger)`, `raw_debt = expected - credited`, and applies the carry-over policy's decay to produce `effective_debt`. Debt is the input to the equity-deficit score component for entitlement-driven dimensions.

## Training plans

Parallel to job plans but for development: a `training_plan` carries `development_goal` rows, each with typed `goal_requirement` entries (procedure count, supervised assessment, signoff, CPD hours, tenant custom).

The `goal_progress` reactive view rolls staff clinical activity + CPD credits + assessments against goal requirements. When the solver has a choice between two candidates and one has an open training goal that this slot could contribute to, the training-plan pull raises that candidate's score.

Training plans have a workflow-driven approval: `draft → proposed → approved → active → paused → achieved → withdrawn`. Signoff is dual-pathway — supervisor + training lead — and the signoff trail is stored in `signed_off_by_jsonb` for audit.

## Feedback loops

The feedback subsystem (`feedback_thread` + `feedback_message`) attaches a discussion thread to any BPP-EQUITY artefact — an allocation policy, a job plan, a training plan, an equity flag, a job-plan entitlement, an equity adjustment. When a staff member disputes a scoring decision, the thread carries the discussion + resolution + audit trail.

State machine: `open → resolved → reopened`. SLA fields track response time. A worker posts with `feedback.post` (held by `staff_member`) and raises a fairness flag with `equity.flag`; replying and resolving needs `feedback.respond` and `equity.respond`, held by `roster_area_admin` and `tenant_admin`. Publishing a policy needs `equity.policy.publish`, held by `tenant_admin` alone.

## Adjustments

`equity_adjustment` rows are the discrimination-risk mitigation: standing-policy overrides that exclude a staff from a dimension, weight their denominator, weight their numerator, or pause their equity accounting entirely (e.g. during long-term sickness).

Every adjustment carries a `signed_off_by` field (NOT NULL) — the compliance officer or clinical director who authorised the exception. Soft-delete via a revoke triplet. Adjustments show on the affected staff's job-plan detail page.

## Where to look

* Policy: sidebar → **Fairness & cover** → **Allocation policies** (`/ui/admin/allocation-policies`).
* Workload plans: sidebar → **Job planning** → **Workload plans** (`/ui/admin/workload-plans`), or from a staff record. ⓘ The screens say **Workload plan**; this documentation and the database say *job plan*. Same thing.
* Training plans: sidebar → **People management** → **Training plans** (`/ui/admin/training-plans`), or from a staff record.
* Ledger inspection: open a workload plan from `/ui/admin/workload-plans` and use its ledger view.
* Fairness surfaces: sidebar → **Fairness & cover**, which holds **Allocation Equity**, **Equity Flags** (`/ui/admin/equity-flags`), **Equity events (audit log)** (`/ui/admin/equity-events`) and **Equity adjustments (overrides)** (`/ui/admin/equity-adjustments`).
* Feedback threads: a worker's own threads are at `/ui/my-resouris/feedback`. ⚠️ There is no admin list of feedback threads — an administrator reaches a disputed decision through **Equity Flags** (`/ui/admin/equity-flags`) and replies from the thread itself.


---

# 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/equity-model.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.
