Crescent Finance Ops — Solver Skill
Environment
All API calls use HTTP against the remote base URL from environment_access.md / payloads/environment_access.json. Each task payload contains a base_url field that may reference localhost; ignore it — use the remote URL from environment_access.md instead.
Remote base: http://<host>:<port> (see environment_access.md).
Verify connectivity with GET /health → {"status":"ok"}.
Finance Ops — Branch & Regional Reporting
Endpoints
| Endpoint |
Description |
GET /api/finance/branches |
All 12 branches with branch_id, branch_name, region_id, region_name |
GET /api/finance/period-map |
Period-to-FY mapping: M1–M12 = FY2024, M13–M24 = FY2025 |
GET /api/finance/accounts |
14 accounts with account, category, display_name, metric_type |
GET /api/finance/records |
168 records: for each (account, branch_id) a dict of {period: value} |
Account Categories
| Account |
Category |
Used For |
product_revenue |
revenue |
Revenue |
service_revenue |
revenue |
Revenue |
direct_materials_cogs |
cogs |
COGS |
direct_labor_cogs |
cogs |
COGS |
sales_sga |
sga |
SG&A |
admin_sga |
sga |
SG&A |
occupancy_sga |
sga |
SG&A |
shared_service_allocations |
allocations |
Allocations |
orders |
operating |
Operating metrics |
revenue_units |
operating |
Operating metrics |
active_customers |
operating |
ARPU denominator |
labor_headcount |
operating |
Sales-per-labor denominator |
admin_headcount |
operating |
Operating metrics |
backlog |
operating |
Operating metrics |
Income Statement Formulas
revenue = product_revenue + service_revenue
cogs = direct_materials_cogs + direct_labor_cogs
gross_margin = revenue - cogs
sga = sales_sga + admin_sga + occupancy_sga
ebitda = gross_margin - sga - shared_service_allocations
ebitda_margin = ebitda / revenue
arpu = revenue / sum(active_customers over all periods in scope)
sales_per_labor_headcount = revenue / sum(labor_headcount over all periods in scope)
Period Conventions
- Single month: lookup
records[account][branch_id].values[period]. Example: M24 is the current close period (Dec FY2025), M23 is the prior period (Nov FY2025).
- Fiscal year aggregates: sum all 12 periods in the fiscal year.
- FY2024: M1 through M12
- FY2025: M13 through M24
- MoM variance:
amount = current_revenue - prior_revenue, pct = amount / prior_revenue.
- period_convention output: maps period ranges to fiscal year strings:
M1_to_M12: "FY2024"
M13_to_M24: "FY2025"
current_month / prior_month: use the period identifiers (e.g. "M24", "M23")
ARPU and Sales-Per-Labor-Headcount
These are FY-level ratios only, not monthly. The denominators are sums of the headcount/customer counts over all periods in the fiscal year (not averages).
Branch Rankings
- Sales growth rank: rank all 12 branches descending by FY2025/FY2024 revenue growth. Rank 1 = highest growth. Return the 1-based index (not 0-based).
- EBITDA rank within region: rank branches in the same region descending by FY2025 EBITDA. Rank 1 = highest EBITDA. Return the 1-based index.
- Top sales growth / top ARPU: the branch_id at rank 1 of the respective descending sort.
Region Context
- Region branch IDs: filter
GET /api/finance/branches by region_id, sort ascending.
- Region EBITDA: sum FY2025 EBITDA of all branches in the region.
- Reconciliation variance:
region_ebitda - sum(branch_ebitda). Should be zero since records are additive by construction.
Compensation Ops — Current-Year Summaries & Forecasts
Endpoints
| Endpoint |
Description |
GET /api/compensation/rate-book |
MWS, seniority bands, title percentages, quarter weeks, business rules |
GET /api/compensation/rosters |
109 roster rows across 4 ensembles with employee details |
GET /api/compensation/scenarios |
4 forecast scenarios with year+1/year+2 growth rates |
Rate Book Fields
minimum_weekly_scale (e.g. 2520.00) — base MWS rate
quarter_weeks — always {"Q1":13, "Q2":13, "Q3":13, "Q4":13} for current year
seniority_weekly — list of bands: {min_years, max_years, weekly_amount}. max_years: null means unbounded upper.
title_premium_pct — dict mapping title name to percentage (applied to MWS). Titles: Concertmaster (22%), Principal (20%), Section Lead (15%), Associate Principal (10%), Assistant Principal (10%).
current_year — integer (e.g. 2026)
pay_types — ordered list: ["Minimum Weekly Scale", "Titled Position Premium", "Seniority", "Overscale"]
business_rules — three critical rules (see below)
Business Rules (exact text)
- "Use roster quarter weeks, not a fixed 13-week quarter, when partial-quarter employees are listed."
- "If combined_overscale_includes_title is true, do not add a titled position premium separately for that employee."
- "For forecast years, add one year of service for Year + 1 and two years of service for Year + 2 before assigning seniority bands."
Roster Fields
employee_id, ensemble_id, ensemble_name
title — string or null. Maps to title_premium_pct keys.
years_of_service — integer for current year seniority band lookup
overscale_weekly — additional weekly dollar amount
combined_overscale_includes_title — if true, skip titled position premium for this employee
weeks_by_quarter — {Q1: N, Q2: N, Q3: N, Q4: N}. Not always 13; partial quarters are possible.
notes — string, sometimes empty
Per-Employee Compensation Formula
For each employee, for each quarter:
mws_pay = minimum_weekly_scale * quarter_weeks
title_pct = title_premium_pct[title] (0 if title is null)
title_pay = minimum_weekly_scale * title_pct * quarter_weeks
← SKIP entirely if combined_overscale_includes_title is true
seniority_weekly = seniority_weekly[band_for(years_of_service)]
seniority_pay = seniority_weekly * quarter_weeks
overscale_pay = overscale_weekly * quarter_weeks
Total for the quarter = mws_pay + title_pay + seniority_pay + overscale_pay.
Seniority Band Lookup
Find the band where min_years <= years_of_service <= max_years. The max_years: null band matches all years ≥ min_years. The 0–4 year band has weekly_amount: 0.0 (no seniority pay for junior employees).
Treatment Counts
- combined_overscale_employee_count: count of roster rows where
combined_overscale_includes_title == true. Count them even if overscale_weekly is 0.
- partial_quarter_employee_count: count of roster rows where any quarter has
weeks != 13.
Forecast (Multi-Year) Extensions
Scenario pull
Fetch GET /api/compensation/scenarios, select by scenario_id. Each scenario has year_plus_1 and year_plus_2 blocks, each containing:
mws_growth — growth rate applied to base MWS
overscale_growth — growth rate applied to each employee's overscale_weekly
seniority_growth — growth rate applied to each band's weekly_amount
title_pct_multiplier — multiplier on the title percentage (e.g. 1.0 = no change, 0.98 = reduce by 2%)
Forecast year computation
For each forecast year Y+1 or Y+2:
forecast_mws = base_mws * (1 + scenario.mws_growth)
forecast_overscale = employee.overscale_weekly * (1 + scenario.overscale_growth)
forecast_seniority_bands = each band.weekly_amount * (1 + scenario.seniority_growth)
forecast_title_pct = base_title_pct * scenario.title_pct_multiplier
forecast_yos = employee.years_of_service + offset (1 for Y+1, 2 for Y+2)
Then apply the per-employee formula using the forecast values.
Critical: Growth rates are applied to the base (current-year) values independently for each year, not compounded. The Y+2 rates are not applied on top of the Y+1 rates.
Largest growth pay type
Compute each pay type's absolute dollar growth from current year to Year+2: Y2_pay_type_total - current_pay_type_total. The type with the largest positive difference is the largest growth pay type. Return the enum value (e.g. "Minimum Weekly Scale").
Note: This is based on absolute dollar change, not percentage growth. A pay type with a small base but large percentage change will not beat a large-base pay type with moderate percentage growth. Compare Y+2_total - current_total across all four pay types.
Payroll Ops — Weekly Payroll Review
Endpoints
| Endpoint |
Description |
GET /api/payroll/rate-book |
Service rates, premium percentages, conflict thresholds, weekly guarantee |
GET /api/payroll/productions |
17 productions, each with schedule and roster |
Rate Book Fields
service_rates:
Rehearsal: 58.75 (hourly)
Performance: 260.25 (per service)
Audit: 260.25 (per service)
1hr Sound Check: 80.00 (per service)
2hr Sound Check: 142.50 (per service)
premium_pct:
principal_or_lead: 0.15
electronic: 0.25
quartet: 0.15
concertmaster: 0.20
first_double: 0.25
additional_double: 0.10
vacation: 0.04
conflict_thresholds:
rehearsal_earliest_start: "09:00"
rehearsal_latest_end: "18:30"
service_time_limits:
Rehearsal: 5.0
1hr Sound Check: 1.0
2hr Sound Check: 2.0
weekly_guarantee: 2082.00
Business Rules (exact text)
- "Service rates and premiums come from this rate book."
- "Rehearsal pay is hourly with a three-hour minimum call."
- "Performance, audit, and sound-check rates are per service."
- "Premiums are applied to the musician's base service pay before vacation."
- "The doubles premium is 25% for the first extra instrument and 10% for each additional extra instrument."
- "Vacation is 4% of base service pay plus premiums when vacation_eligible is true."
- "A weekly guarantee adjustment applies only to guaranteed regular players when base service pay is below weekly_guarantee."
Production Structure
Each production has:
production_id, title, week_start
schedule — list of service objects: {service_id, date, service_type, start_time, end_time, duration_hours}
roster — list of musician objects: {musician_id, name, instrument, assigned_service_ids[], lead, principal, quartet, electronic, doubles, substitute, vacation_eligible}
Per-Service Pay Calculation
1. Base rate:
- Rehearsal: 58.75 * max(3.0, duration_hours)
- Performance: 260.25
- Audit: 260.25
- 1hr Sound Check: 80.00
- 2hr Sound Check: 142.50
2. Premiums (non-doubles, computed on base rate):
- principal_or_lead: 15% if principal==true OR lead==true
- electronic: 25% if electronic==true
- quartet: 15% if quartet==true
- concertmaster: 20% if concertmaster==true (rare; never triggered in train data)
All non-doubles premiums sum to a single premium pool.
3. Doubles premium (computed on base rate):
- first_double: 25% if doubles >= 1
- additional_double: 10% × (doubles − 1) if doubles >= 2
Doubles go into a separate "doubles" category.
4. Vacation: 4% × (base_rate + all_premiums + all_doubles)
Only if vacation_eligible == true.
5. Service total = base_rate + premiums + doubles + vacation
Category Totals (top-level aggregation)
| Category Key |
What Goes In |
performance |
Sum of all Performance base rates |
audit |
Sum of all Audit base rates |
rehearsal |
Sum of all Rehearsal base rates (after 3hr minimum) |
sound_check |
Sum of all 1hr + 2hr Sound Check base rates |
premium |
Sum of all non-doubles premiums (principal/lead, electronic, quartet, concertmaster) |
doubles |
Sum of all doubles premiums (first_double + additional) |
vacation |
Sum of all vacation amounts |
guarantee_adjustment |
Sum of weekly guarantee top-ups |
substitute_adjustment |
0.0 when no substitute-specific rate differential applies (from rate book rules, substitutes simply get no vacation and no guarantee) |
Weekly Guarantee
After computing each non-substitute musician's total pay across all services, if total < 2082.00, add a guarantee_adjustment of 2082.00 - total. The musician's total becomes 2082.00. Substitutes are excluded from the guarantee.
Per-Musician Totals
- Ordered by
musician_id ascending.
- Include only nonzero category amounts in the per-musician
categories object.
top_paid_musician_id = the musician_id with the highest total.
Service Counts
Count occurrences of each service_type in the schedule. Keys use the exact service_type strings: "Rehearsal", "1hr Sound Check", "2hr Sound Check", "Performance", "Audit".
Conflict Flags (return sorted alphabetically)
| Flag |
Condition |
REHEARSAL_EARLY_START |
Any Rehearsal with start_time < "09:00" |
REHEARSAL_LATE_END |
Any Rehearsal with end_time > "18:30" |
SERVICE_OVER_TIME_LIMIT |
Any Rehearsal with duration_hours > 5.0 |
SOUND_CHECK_DURATION_MISMATCH |
Any 1hr/2hr Sound Check where duration_hours != expected (1.0 or 2.0) |
Return only flags that actually trigger. If none trigger, return [].
Rounding & Output Conventions (all modules)
| Type |
Precision |
Example |
Currency (currency) |
2 decimal places |
round(val, 2) → 12345.67 |
Percent / ratio (decimal percent) |
4 decimal places |
round(val, 4) → 0.0966 (NOT 9.66%) |
| Lists of IDs |
Ascending string sort unless rank order specified |
["BR-004", "BR-005", "BR-006"] |
per_musician list |
Ascending by musician_id string |
|
conflict_flags list |
Alphabetically sorted |
["REHEARSAL_EARLY_START", "SERVICE_OVER_TIME_LIMIT"] |
Percent representation: Growth rates, EBITDA margin, and other ratio fields are expressed as decimals (e.g. 0.0966 not 9.66%).
Common Pitfalls
Finance
- Revenue is two accounts: Always sum
product_revenue + service_revenue. Missing one will understate revenue.
- EBITDA excludes allocations:
ebitda = revenue - cogs - sga - allocations. Do not forget allocations.
- ARPU / Sales-per-labor are FY-level only: The denominators are sums of the count metrics across all 12 periods in the fiscal year, not monthly averages.
- Reconciliation variance should be zero: Region totals are the sum of branch totals. Any nonzero variance indicates a computation error.
Compensation
- combined_overscale_includes_title: When
true, skip the Titled Position Premium for that employee entirely. The overscale amount already bundles the title premium. Do not double-count.
- Title is null: When
title is null, title_pct is 0. The employee gets no Titled Position Premium (affects computation even without combined_overscale_includes_title).
- Forecast YoS offsets: Add +1 for Y+1, +2 for Y+2 before looking up seniority bands. An employee with 4 YoS becomes 5 YoS in Y+1, potentially crossing into a new band.
- Forecast rates are non-compounded: Each year's growth rates apply to the base (current year) values, not to the previous forecast year.
- Partial quarter detection: Partial quarters use roster
weeks_by_quarter (which can differ from the fixed 13-week standard). Check all four quarters.
- Pay type order: Always use the rate book's
pay_types list: ["Minimum Weekly Scale", "Titled Position Premium", "Seniority", "Overscale"].
Payroll
- Rehearsal 3-hour minimum:
max(3.0, duration_hours) before multiplying by hourly rate. A 5.5hr rehearsal pays for 5.5hrs (not 3), a 2.5hr rehearsal pays for 3hrs.
- Premium vs Doubles categories: These are separate category totals. Principal/lead, electronic, quartet premiums go in
premium. First_double and additional_double go in doubles. The vacation calculation includes BOTH, but the category totals keep them split.
- No concertmaster flag in train data: The premium exists in the rate book but the roster flag was not observed in any production.
- Substitutes: No vacation eligibility, no weekly guarantee.
substitute_adjustment category = 0.0 when no substitute rate differential applies (the rate book specifies no substitute rate).
- Sound check types: Both
"1hr Sound Check" and "2hr Sound Check" map to the sound_check category. Service counts use the full type string as key.
- Per-musician categories: Only include categories with nonzero amounts. The
guarantee_adjustment appears only for those who receive the top-up.
1---2name: self-attempt-01-483description: Crescent Finance Ops — Solver Skill4---5# Crescent Finance Ops — Solver Skill67## Environment89All API calls use HTTP against the remote base URL from `environment_access.md` / `payloads/environment_access.json`. Each task payload contains a `base_url` field that may reference localhost; **ignore it** — use the remote URL from `environment_access.md` instead.1011**Remote base:** `http://<host>:<port>` (see `environment_access.md`).1213Verify connectivity with `GET /health` → `{"status":"ok"}`.1415---1617## Finance Ops — Branch & Regional Reporting1819### Endpoints2021| Endpoint | Description |22|---|---|23| `GET /api/finance/branches` | All 12 branches with `branch_id`, `branch_name`, `region_id`, `region_name` |24| `GET /api/finance/period-map` | Period-to-FY mapping: M1–M12 = FY2024, M13–M24 = FY2025 |25| `GET /api/finance/accounts` | 14 accounts with `account`, `category`, `display_name`, `metric_type` |26| `GET /api/finance/records` | 168 records: for each `(account, branch_id)` a dict of `{period: value}` |2728### Account Categories2930| Account | Category | Used For |31|---|---|---|32| `product_revenue` | revenue | Revenue |33| `service_revenue` | revenue | Revenue |34| `direct_materials_cogs` | cogs | COGS |35| `direct_labor_cogs` | cogs | COGS |36| `sales_sga` | sga | SG&A |37| `admin_sga` | sga | SG&A |38| `occupancy_sga` | sga | SG&A |39| `shared_service_allocations` | allocations | Allocations |40| `orders` | operating | Operating metrics |41| `revenue_units` | operating | Operating metrics |42| `active_customers` | operating | ARPU denominator |43| `labor_headcount` | operating | Sales-per-labor denominator |44| `admin_headcount` | operating | Operating metrics |45| `backlog` | operating | Operating metrics |4647### Income Statement Formulas4849```50revenue = product_revenue + service_revenue51cogs = direct_materials_cogs + direct_labor_cogs52gross_margin = revenue - cogs53sga = sales_sga + admin_sga + occupancy_sga54ebitda = gross_margin - sga - shared_service_allocations55ebitda_margin = ebitda / revenue56arpu = revenue / sum(active_customers over all periods in scope)57sales_per_labor_headcount = revenue / sum(labor_headcount over all periods in scope)58```5960### Period Conventions6162- **Single month:** lookup `records[account][branch_id].values[period]`. Example: M24 is the current close period (Dec FY2025), M23 is the prior period (Nov FY2025).63- **Fiscal year aggregates:** sum all 12 periods in the fiscal year.64 - FY2024: M1 through M1265 - FY2025: M13 through M2466- **MoM variance:** `amount = current_revenue - prior_revenue`, `pct = amount / prior_revenue`.67- **period_convention output:** maps period ranges to fiscal year strings:68 - `M1_to_M12`: `"FY2024"`69 - `M13_to_M24`: `"FY2025"`70 - `current_month` / `prior_month`: use the period identifiers (e.g. `"M24"`, `"M23"`)7172### ARPU and Sales-Per-Labor-Headcount7374These are **FY-level ratios** only, not monthly. The denominators are **sums** of the headcount/customer counts over all periods in the fiscal year (not averages).7576### Branch Rankings7778- **Sales growth rank:** rank all 12 branches descending by FY2025/FY2024 revenue growth. Rank 1 = highest growth. Return the **1-based index** (not 0-based).79- **EBITDA rank within region:** rank branches in the same region descending by FY2025 EBITDA. Rank 1 = highest EBITDA. Return the 1-based index.80- **Top sales growth / top ARPU:** the branch_id at rank 1 of the respective descending sort.8182### Region Context8384- **Region branch IDs:** filter `GET /api/finance/branches` by `region_id`, sort ascending.85- **Region EBITDA:** sum FY2025 EBITDA of all branches in the region.86- **Reconciliation variance:** `region_ebitda - sum(branch_ebitda)`. Should be zero since records are additive by construction.8788---8990## Compensation Ops — Current-Year Summaries & Forecasts9192### Endpoints9394| Endpoint | Description |95|---|---|96| `GET /api/compensation/rate-book` | MWS, seniority bands, title percentages, quarter weeks, business rules |97| `GET /api/compensation/rosters` | 109 roster rows across 4 ensembles with employee details |98| `GET /api/compensation/scenarios` | 4 forecast scenarios with year+1/year+2 growth rates |99100### Rate Book Fields101102- `minimum_weekly_scale` (e.g. 2520.00) — base MWS rate103- `quarter_weeks` — always `{"Q1":13, "Q2":13, "Q3":13, "Q4":13}` for current year104- `seniority_weekly` — list of bands: `{min_years, max_years, weekly_amount}`. `max_years: null` means unbounded upper.105- `title_premium_pct` — dict mapping title name to percentage (applied to MWS). Titles: Concertmaster (22%), Principal (20%), Section Lead (15%), Associate Principal (10%), Assistant Principal (10%).106- `current_year` — integer (e.g. 2026)107- `pay_types` — ordered list: `["Minimum Weekly Scale", "Titled Position Premium", "Seniority", "Overscale"]`108- `business_rules` — three critical rules (see below)109110### Business Rules (exact text)1111121. "Use roster quarter weeks, not a fixed 13-week quarter, when partial-quarter employees are listed."1132. "If combined_overscale_includes_title is true, do not add a titled position premium separately for that employee."1143. "For forecast years, add one year of service for Year + 1 and two years of service for Year + 2 before assigning seniority bands."115116### Roster Fields117118- `employee_id`, `ensemble_id`, `ensemble_name`119- `title` — string or `null`. Maps to `title_premium_pct` keys.120- `years_of_service` — integer for current year seniority band lookup121- `overscale_weekly` — additional weekly dollar amount122- `combined_overscale_includes_title` — if `true`, skip titled position premium for this employee123- `weeks_by_quarter` — `{Q1: N, Q2: N, Q3: N, Q4: N}`. Not always 13; partial quarters are possible.124- `notes` — string, sometimes empty125126### Per-Employee Compensation Formula127128For each employee, for each quarter:129130```131mws_pay = minimum_weekly_scale * quarter_weeks132133title_pct = title_premium_pct[title] (0 if title is null)134title_pay = minimum_weekly_scale * title_pct * quarter_weeks135 ← SKIP entirely if combined_overscale_includes_title is true136137seniority_weekly = seniority_weekly[band_for(years_of_service)]138seniority_pay = seniority_weekly * quarter_weeks139140overscale_pay = overscale_weekly * quarter_weeks141```142143Total for the quarter = mws_pay + title_pay + seniority_pay + overscale_pay.144145### Seniority Band Lookup146147Find the band where `min_years <= years_of_service <= max_years`. The `max_years: null` band matches all years ≥ `min_years`. The 0–4 year band has `weekly_amount: 0.0` (no seniority pay for junior employees).148149### Treatment Counts150151- **combined_overscale_employee_count:** count of roster rows where `combined_overscale_includes_title == true`. Count them even if `overscale_weekly` is 0.152- **partial_quarter_employee_count:** count of roster rows where any quarter has `weeks != 13`.153154### Forecast (Multi-Year) Extensions155156#### Scenario pull157158Fetch `GET /api/compensation/scenarios`, select by `scenario_id`. Each scenario has `year_plus_1` and `year_plus_2` blocks, each containing:159- `mws_growth` — growth rate applied to base MWS160- `overscale_growth` — growth rate applied to each employee's `overscale_weekly`161- `seniority_growth` — growth rate applied to each band's `weekly_amount`162- `title_pct_multiplier` — multiplier on the title percentage (e.g. 1.0 = no change, 0.98 = reduce by 2%)163164#### Forecast year computation165166For each forecast year Y+1 or Y+2:167168```169forecast_mws = base_mws * (1 + scenario.mws_growth)170forecast_overscale = employee.overscale_weekly * (1 + scenario.overscale_growth)171forecast_seniority_bands = each band.weekly_amount * (1 + scenario.seniority_growth)172forecast_title_pct = base_title_pct * scenario.title_pct_multiplier173forecast_yos = employee.years_of_service + offset (1 for Y+1, 2 for Y+2)174```175176Then apply the per-employee formula using the forecast values.177178**Critical:** Growth rates are applied to the base (current-year) values independently for each year, not compounded. The Y+2 rates are not applied on top of the Y+1 rates.179180#### Largest growth pay type181182Compute each pay type's absolute dollar growth from current year to Year+2: `Y2_pay_type_total - current_pay_type_total`. The type with the largest positive difference is the largest growth pay type. Return the enum value (e.g. `"Minimum Weekly Scale"`).183184**Note:** This is based on **absolute dollar change**, not percentage growth. A pay type with a small base but large percentage change will not beat a large-base pay type with moderate percentage growth. Compare `Y+2_total - current_total` across all four pay types.185186---187188## Payroll Ops — Weekly Payroll Review189190### Endpoints191192| Endpoint | Description |193|---|---|194| `GET /api/payroll/rate-book` | Service rates, premium percentages, conflict thresholds, weekly guarantee |195| `GET /api/payroll/productions` | 17 productions, each with `schedule` and `roster` |196197### Rate Book Fields198199```200service_rates:201 Rehearsal: 58.75 (hourly)202 Performance: 260.25 (per service)203 Audit: 260.25 (per service)204 1hr Sound Check: 80.00 (per service)205 2hr Sound Check: 142.50 (per service)206207premium_pct:208 principal_or_lead: 0.15209 electronic: 0.25210 quartet: 0.15211 concertmaster: 0.20212 first_double: 0.25213 additional_double: 0.10214 vacation: 0.04215216conflict_thresholds:217 rehearsal_earliest_start: "09:00"218 rehearsal_latest_end: "18:30"219220service_time_limits:221 Rehearsal: 5.0222 1hr Sound Check: 1.0223 2hr Sound Check: 2.0224225weekly_guarantee: 2082.00226```227228### Business Rules (exact text)2292301. "Service rates and premiums come from this rate book."2312. "Rehearsal pay is hourly with a three-hour minimum call."2323. "Performance, audit, and sound-check rates are per service."2334. "Premiums are applied to the musician's base service pay before vacation."2345. "The doubles premium is 25% for the first extra instrument and 10% for each additional extra instrument."2356. "Vacation is 4% of base service pay plus premiums when vacation_eligible is true."2367. "A weekly guarantee adjustment applies only to guaranteed regular players when base service pay is below weekly_guarantee."237238### Production Structure239240Each production has:241- `production_id`, `title`, `week_start`242- `schedule` — list of service objects: `{service_id, date, service_type, start_time, end_time, duration_hours}`243- `roster` — list of musician objects: `{musician_id, name, instrument, assigned_service_ids[], lead, principal, quartet, electronic, doubles, substitute, vacation_eligible}`244245### Per-Service Pay Calculation246247```2481. Base rate:249 - Rehearsal: 58.75 * max(3.0, duration_hours)250 - Performance: 260.25251 - Audit: 260.25252 - 1hr Sound Check: 80.00253 - 2hr Sound Check: 142.502542552. Premiums (non-doubles, computed on base rate):256 - principal_or_lead: 15% if principal==true OR lead==true257 - electronic: 25% if electronic==true258 - quartet: 15% if quartet==true259 - concertmaster: 20% if concertmaster==true (rare; never triggered in train data)260 All non-doubles premiums sum to a single premium pool.2612623. Doubles premium (computed on base rate):263 - first_double: 25% if doubles >= 1264 - additional_double: 10% × (doubles − 1) if doubles >= 2265 Doubles go into a separate "doubles" category.2662674. Vacation: 4% × (base_rate + all_premiums + all_doubles)268 Only if vacation_eligible == true.2692705. Service total = base_rate + premiums + doubles + vacation271```272273### Category Totals (top-level aggregation)274275| Category Key | What Goes In |276|---|---|277| `performance` | Sum of all Performance base rates |278| `audit` | Sum of all Audit base rates |279| `rehearsal` | Sum of all Rehearsal base rates (after 3hr minimum) |280| `sound_check` | Sum of all 1hr + 2hr Sound Check base rates |281| `premium` | Sum of all non-doubles premiums (principal/lead, electronic, quartet, concertmaster) |282| `doubles` | Sum of all doubles premiums (first_double + additional) |283| `vacation` | Sum of all vacation amounts |284| `guarantee_adjustment` | Sum of weekly guarantee top-ups |285| `substitute_adjustment` | 0.0 when no substitute-specific rate differential applies (from rate book rules, substitutes simply get no vacation and no guarantee) |286287### Weekly Guarantee288289After computing each non-substitute musician's total pay across all services, if `total < 2082.00`, add a `guarantee_adjustment` of `2082.00 - total`. The musician's total becomes 2082.00. Substitutes are excluded from the guarantee.290291### Per-Musician Totals292293- Ordered by `musician_id` ascending.294- Include only nonzero category amounts in the per-musician `categories` object.295- `top_paid_musician_id` = the musician_id with the highest total.296297### Service Counts298299Count occurrences of each `service_type` in the schedule. Keys use the exact service_type strings: `"Rehearsal"`, `"1hr Sound Check"`, `"2hr Sound Check"`, `"Performance"`, `"Audit"`.300301### Conflict Flags (return sorted alphabetically)302303| Flag | Condition |304|---|---|305| `REHEARSAL_EARLY_START` | Any Rehearsal with `start_time < "09:00"` |306| `REHEARSAL_LATE_END` | Any Rehearsal with `end_time > "18:30"` |307| `SERVICE_OVER_TIME_LIMIT` | Any Rehearsal with `duration_hours > 5.0` |308| `SOUND_CHECK_DURATION_MISMATCH` | Any 1hr/2hr Sound Check where `duration_hours != expected` (1.0 or 2.0) |309310Return only flags that actually trigger. If none trigger, return `[]`.311312---313314## Rounding & Output Conventions (all modules)315316| Type | Precision | Example |317|---|---|---|318| Currency (`currency`) | 2 decimal places | `round(val, 2)` → `12345.67` |319| Percent / ratio (`decimal percent`) | 4 decimal places | `round(val, 4)` → `0.0966` (NOT `9.66%`) |320| Lists of IDs | Ascending string sort unless rank order specified | `["BR-004", "BR-005", "BR-006"]` |321| `per_musician` list | Ascending by `musician_id` string | |322| `conflict_flags` list | Alphabetically sorted | `["REHEARSAL_EARLY_START", "SERVICE_OVER_TIME_LIMIT"]` |323324**Percent representation:** Growth rates, EBITDA margin, and other ratio fields are expressed as **decimals** (e.g. `0.0966` not `9.66%`).325326---327328## Common Pitfalls329330### Finance331- **Revenue is two accounts:** Always sum `product_revenue` + `service_revenue`. Missing one will understate revenue.332- **EBITDA excludes allocations:** `ebitda = revenue - cogs - sga - allocations`. Do not forget allocations.333- **ARPU / Sales-per-labor are FY-level only:** The denominators are sums of the count metrics across all 12 periods in the fiscal year, not monthly averages.334- **Reconciliation variance should be zero:** Region totals are the sum of branch totals. Any nonzero variance indicates a computation error.335336### Compensation337- **combined_overscale_includes_title:** When `true`, **skip** the Titled Position Premium for that employee entirely. The overscale amount already bundles the title premium. Do not double-count.338- **Title is null:** When `title` is `null`, `title_pct` is 0. The employee gets no Titled Position Premium (affects computation even without `combined_overscale_includes_title`).339- **Forecast YoS offsets:** Add +1 for Y+1, +2 for Y+2 before looking up seniority bands. An employee with 4 YoS becomes 5 YoS in Y+1, potentially crossing into a new band.340- **Forecast rates are non-compounded:** Each year's growth rates apply to the **base** (current year) values, not to the previous forecast year.341- **Partial quarter detection:** Partial quarters use roster `weeks_by_quarter` (which can differ from the fixed 13-week standard). Check all four quarters.342- **Pay type order:** Always use the rate book's `pay_types` list: `["Minimum Weekly Scale", "Titled Position Premium", "Seniority", "Overscale"]`.343344### Payroll345- **Rehearsal 3-hour minimum:** `max(3.0, duration_hours)` before multiplying by hourly rate. A 5.5hr rehearsal pays for 5.5hrs (not 3), a 2.5hr rehearsal pays for 3hrs.346- **Premium vs Doubles categories:** These are **separate** category totals. Principal/lead, electronic, quartet premiums go in `premium`. First_double and additional_double go in `doubles`. The vacation calculation includes BOTH, but the category totals keep them split.347- **No concertmaster flag in train data:** The premium exists in the rate book but the roster flag was not observed in any production.348- **Substitutes:** No vacation eligibility, no weekly guarantee. `substitute_adjustment` category = 0.0 when no substitute rate differential applies (the rate book specifies no substitute rate).349- **Sound check types:** Both `"1hr Sound Check"` and `"2hr Sound Check"` map to the `sound_check` category. Service counts use the full type string as key.350- **Per-musician categories:** Only include categories with nonzero amounts. The `guarantee_adjustment` appears only for those who receive the top-up.