Compensation Tools
Per-discipline, per-level salary bands, so an owner can see whether a person is paid correctly for their level — plus the employer cost multiplier that separates gross pay from what the agency actually spends on someone, and the ability to seed a discipline's bands from published government wage data for the agency's own region.
Pay itself is always derived from the cost rate already recorded for each person — these tools only read and band it; they never enter a salary figure separately.
All compensation tools are Owner/Admin only and console-only — not reachable from Slack; use the web console.
list_discipline_level_bands
List every discipline's salary bands across all four levels.
Reads from: discipline compensation bands
Parameters: requesting_user_slack_id
Returns: one row per (discipline, level) pair that has a band set — discipline_id, level (1–4), salary_min, salary_max.
set_discipline_level_band
Set the salary band for one discipline at one level.
Console-only — not reachable from Slack; use the web console.
Writes to: discipline compensation bands
Parameters:
| Param | Required | Description |
|---|---|---|
requesting_user_slack_id | ✅ | |
discipline_id | ✅ | Must be an active discipline |
level | ✅ | Integer, 1–4 |
salary_min | Non-negative | |
salary_max | Non-negative, must be ≥ salary_min when both are set |
Leaving salary_min/salary_max empty clears that band's edge.
A discipline's four levels are contiguous by design — see set_discipline_ladder for setting all four at once as a single connected ladder rather than one band at a time.
set_discipline_ladder
Set a discipline's entire 1–4 salary ladder in one call, as five connected boundaries rather than four independent bands.
Console-only — not reachable from Slack; use the web console.
Writes to: discipline compensation bands (all four levels, in one transaction)
Parameters:
| Param | Required | Description |
|---|---|---|
requesting_user_slack_id | ✅ | |
discipline_id | ✅ | Must be an active discipline |
floor | Level 1's minimum | |
boundary_12 | Level 1's maximum / Level 2's minimum | |
boundary_23 | Level 2's maximum / Level 3's minimum | |
boundary_34 | Level 3's maximum / Level 4's minimum | |
ceiling | Level 4's maximum |
The five boundaries must be strictly ascending (floor < boundary_12 < boundary_23 < boundary_34 < ceiling), or all five must be left empty to clear the ladder entirely. Any other mix — a partial set, or boundaries out of order — is rejected and nothing is written.
On success, four bands are written that share their adjacent endpoints exactly: Level 1 is floor–boundary_12, Level 2 is boundary_12–boundary_23, and so on through Level 4 at boundary_34–ceiling.
set_employer_cost_multiplier
Set the tenant's employer cost multiplier — for every $1 of gross pay, what the agency actually spends once employer payroll taxes, benefits, and retirement match are added.
Console-only — not reachable from Slack; use the web console.
Updates: tenant config
Parameters:
| Param | Required | Description |
|---|---|---|
requesting_user_slack_id | ✅ | |
employer_cost_multiplier | ✅ | Number, 1.0–2.0. 1.0 means recorded cost rates are already gross-pay-only. |
The multiplier covers only costs that scale with a person's pay — it deliberately excludes flat operating costs (software, equipment, office, recruiting) even when those happen to be billed per person.
This value is scoped to the compensation pages, and only to them. It is read on exactly three surfaces, all inside the Users hub: the Bands tab's assumptions panel, the Direct labor tab's Employer cost column, and the Band comparison column on the Rates tab. No project-cost calculation reads it — LER, margin, profit, the portfolio scatter, the forecast, proposal costing and the CSV export are all hours × the person's own rate, for every employment type. Issue #2295 briefly made project cost read this multiplier; issue #2392 (2026-09-22) reversed that, so a change here moves what the compensation pages report and nothing else — see docs/reference/concepts/compensation.md and the "Employer cost multiplier" section of CLAUDE.md.
preview_discipline_ladders
Fetch a proposed salary ladder for every mapped discipline from published wage data, for review before applying. Read-only — fetching never writes anything.
Console-only — not reachable from Slack; use the web console.
Parameters:
| Param | Required | Description |
|---|---|---|
requesting_user_slack_id | ✅ | |
location | "City, ST". Falls back to the tenant's configured HQ location; fails if neither is set. |
Returns: location, and proposals — one result per active discipline. A discipline with no occupation mapped is returned as unmapped without ever being queried. A mapped discipline's result carries a status of ok (with the geographic tier used, the wage anchors, and the resulting ladder), unavailable (no usable data at any geographic tier), data-error (the published data didn't produce a sane ladder), or failed (the lookup itself failed).
Applying a proposed ladder is a separate, explicit step through set_discipline_ladder — nothing here is applied automatically.
set_discipline_soc_code
Map a discipline to a real government occupation code, so its bands can eventually be seeded from published regional wage data.
Console-only — not reachable from Slack; use the web console.
Updates: 08. Disciplines
Parameters:
| Param | Required | Description |
|---|---|---|
requesting_user_slack_id | ✅ | |
discipline_id | ✅ | Must be an active discipline |
soc_code | Must be a recognized occupation code from the committed occupation taxonomy. Empty clears the mapping. |
There is no fuzzy matching — a code that isn't in the recognized taxonomy is rejected, since a wrong match would produce a confident, wrong salary figure. The occupation is always picked explicitly from a grouped list.