Crescent Finance Ops — Solver Skill (task_group_009)
A reusable SOP for the five task families in this group: branch close, compensation current-year, weekly payroll, regional dashboard, compensation forecast. All rules below were confirmed by fetching the live remote environment and computing the train tasks end-to-end. Worked numbers from the train tasks are included ONLY as illustrations of each rule (they are not to be copied verbatim for test tasks — recompute against the live data for any new target id).
0. Environment & shared conventions
Base URL (LIVE remote — always use this)
<remote-env-url>
The staged payloads/environment_access.json already carries this remote base_url. NEVER use 127.0.0.1 / a local dev server — it is not running. Fetch with curl -s $BASE/<endpoint>; all responses are JSON.
Endpoint catalog
| Endpoint |
Returns |
Used by |
GET /health |
{status:ok} sanity check |
all |
GET /api/manifest |
entity lists + record_counts + seed |
discovery |
GET /api/finance/branches |
12 branches w/ canonical branch_id→region_id→region_name |
finance families |
GET /api/finance/period-map |
24 monthly periods M1..M24 → fiscal_year/month_name/month_number |
finance families |
GET /api/finance/accounts |
14 accounts w/ category + metric_type |
finance families |
GET /api/finance/records |
168 rows = 14 accounts × 12 branches; each row = {account, branch_id, branch_name, region_id, values:{M1..M24}} |
finance families |
GET /api/compensation/rate-book |
MWS, pay_types, quarter_weeks, seniority_weekly, title_premium_pct, business_rules, current_year=2026 |
comp families |
GET /api/compensation/rosters |
109 employees across 4 ensembles; per-employee title, years_of_service, overscale_weekly, combined_overscale_includes_title, weeks_by_quarter |
comp families |
GET /api/compensation/scenarios |
4 scenarios; each has year_plus_1/year_plus_2 drivers |
forecast |
GET /api/payroll/rate-book |
service_rates, service_time_limits, premium_pct, weekly_guarantee, conflict_thresholds, business_rules |
payroll |
GET /api/payroll/productions |
17 productions; each = {production_id, title, week_start, roster[], schedule[]} |
payroll |
Useful query filters: ?branch_id=BR-009, ?region=REG-SOUTH, ?account=product_revenue, ?ensemble_id=ENS-REDWOOD, ?production_id=PROD-HAMILTON-26. (It is usually simpler to fetch the whole collection once and index it in Python.)
Rounding rules (follow the field_types label in each answer_template)
- currency → 2 decimals.
- decimal percent / ratio → 4 decimals, expressed as a decimal fraction (e.g.
0.0138, NOT 1.38).
- integer counts → exact integer.
- Lists: ascending stable IDs unless a rank field says otherwise;
conflict_flags sorted alphabetically; per_musician ordered by musician_id.
- ARPU and sales_per_labor_headcount are labeled
currency in the templates → round to 2 decimals (even though they are computed as ratios). ebitda_margin, revenue_growth_pct, ebitda_growth_pct, MoM pct, forecast growth_rates are decimal percent → 4 decimals.
Period → fiscal-year convention (CONFIRMED from period-map)
M1..M12 → FY2024 (Jan..Dec 2024).
M13..M24 → FY2025 (Jan..Dec 2025).
M23 = Nov 2025, M24 = Dec 2025. A fiscal year = 12 consecutive M-periods.
- For
period_convention: M1_to_M12 = "FY2024", M13_to_M24 = "FY2025". current_month/prior_month are period-label strings — use the M-code of the close/prior period (e.g. "M24", "M23"); a "Dec 2025"/"Nov 2025" style is an acceptable alternative if the template expects a human label.
Account categories (CONFIRMED from /api/finance/accounts)
- revenue:
product_revenue, service_revenue
- cogs:
direct_materials_cogs, direct_labor_cogs
- sga:
sales_sga, admin_sga, occupancy_sga
- allocations:
shared_service_allocations
- operating (metric_type =
count, NOT currency — never sum these into revenue/ebitda): orders, revenue_units, active_customers, labor_headcount, admin_headcount, backlog
Income-statement construction (CONFIRMED)
revenue = Σ revenue accounts
cogs = Σ cogs accounts
gross_margin = revenue − cogs
sga = Σ sga accounts
allocations = Σ allocations accounts
ebitda = revenue − cogs − sga − allocations (= gross_margin − sga − allocations). EBITDA always subtracts COGS, even in regional views where the cogs/gross_margin fields are not displayed.
ebitda_margin = ebitda / revenue
arpu = revenue / (Σ active_customers over the same periods)
sales_per_labor_headcount = revenue / (Σ labor_headcount over the same periods)
- operating counts are stored as period values just like currency accounts; sum them across the same periods used for revenue.
Region membership (CONFIRMED from /api/finance/branches — canonical source of truth)
- REG-NORTH: BR-001, BR-002, BR-003
- REG-WEST: BR-004, BR-005, BR-006
- REG-EAST: BR-007, BR-008, BR-011
- REG-SOUTH: BR-009, BR-010, BR-012
- Always derive region membership from
/api/finance/branches. Ignore any stale/draft "background notes" or claimed region in a request memo. Do NOT exclude a branch from its region because a memo says otherwise. The active operations data is authoritative.
branch_ids lists are ascending-sorted.
ebitda_rank_desc / *_rank_desc = descending rank (1 = best/highest). For region ebitda rank, rank the target branch among its region's branches by FY2025 ebitda descending.
Dataset facts (seed 9009)
- 12 branches, 4 regions, 168 finance records (14 accounts × 12 branches; each row carries all 24 M-periods).
- 4 ensembles: ENS-CEDAR (31), ENS-MAPLE (28), ENS-OAK (24), ENS-REDWOOD (26) — 109 roster rows total.
- 17 productions. Payroll roster flags available:
lead, principal, quartet, electronic, doubles, substitute, vacation_eligible (NO concertmaster or guaranteed flag on productions — the concertmaster premium is defined in the rate book but no production roster sets it, so it is $0 in payroll).
1. Family: Branch close (train_001 — BR_CLOSE)
Inputs
request_memo: target_branch_id, close_period (e.g. M24), prior_period (e.g. M23). Reporting focus: income statement, revenue variance, current-FY metrics, regional context, branch rankings.
Step-by-step SOP
- Fetch
/api/finance/branches, /api/finance/period-map, /api/finance/accounts, /api/finance/records. Index records by (branch_id, account) → values[M].
target_branch_id / target_branch_name: from branches list. period_convention: M1-M12→FY2024, M13-M24→FY2025; current_month=close_period code, prior_month=prior_period code.
m24_income_statement (single-period IS for close_period): sum each account category over [close_period] only.
- Worked (BR-004, M24): revenue
293306.29, cogs 108946.99, gross_margin 184359.30, sga 100245.35, allocations 9280.16, ebitda 74833.79.
mom_revenue_variance: amount = revenue(close_period) − revenue(prior_period); pct = amount / revenue(prior_period), 4dp.
- Worked (BR-004): M23 revenue
289310.14, amount 3996.15, pct 0.0138.
fy2025_vs_fy2024:
- FY2024 = sum of M1..M12; FY2025 = sum of M13..M24 (for every category).
fy2025 block: revenue, cogs, gross_margin, sga, allocations, ebitda, ebitda_margin (=fy2025 ebitda/fy2025 revenue, 4dp), arpu (=fy2025 revenue / Σ active_customers M13..M24, currency 2dp), sales_per_labor_headcount (=fy2025 revenue / Σ labor_headcount M13..M24, currency 2dp).
- Worked (BR-004): FY2024 revenue
3177038.53 ebitda 749925.74; FY2025 revenue 3483871.60 ebitda 856610.10 ebitda_margin 0.2459 arpu 999.39 (active_customers=3486) sales_per_labor_hc 24534.31 (labor_hc=142).
revenue_growth_pct = (fy2025_rev − fy2024_rev)/fy2024_rev, 4dp → 0.0966.
ebitda_growth_pct = (fy2025_ebitda − fy2024_ebitda)/fy2024_ebitda, 4dp → 0.1423.
region_context: target branch's region from canonical map. branch_ids = ascending list of that region's branches. fy2025_ebitda = Σ FY2025 ebitda across region branches. ebitda_rank_desc = rank of target branch's FY2025 ebitda within the region, descending (1=best).
- Worked (BR-004, REG-WEST): branch_ids
[BR-004,BR-005,BR-006], fy2025_ebitda 2515012.15, ebitda_rank_desc 2 (BR-005=1, BR-004=2, BR-006=3).
branch_rankings: compute across ALL 12 branches.
sales_growth_rank_desc = descending rank of target branch's annual revenue growth (FY2025 vs FY2024). top_sales_growth_branch_id = branch with max annual revenue growth. top_arpu_branch_id = branch with max FY2025 ARPU.
- Worked (annual): BR-004 rank
2, top_sales_growth BR-002, top_arpu BR-012.
- Decision point — period for "sales growth": primary interpretation is annual (FY25 vs FY24), consistent with
top_arpu using FY2025. If a test task's wording emphasizes the monthly close/MoM, the alternative is MoM (M24 vs M23) revenue growth — under that reading BR-004 ranks 11 and top is still BR-002. Prefer annual unless the task explicitly says "month-over-month".
- Round: currency 2dp, percents/ratios 4dp. Output keys exactly per template.
2. Family: Compensation current-year (train_002 — COMP_CURRENT)
Inputs
request_memo: ensemble_id, summary_type = current_year_by_quarter_and_pay_type.
Rate book constants (CONFIRMED, current_year = 2026)
minimum_weekly_scale (MWS) = 2520.0 /week.
pay_types (use this order): ["Minimum Weekly Scale", "Titled Position Premium", "Seniority", "Overscale"].
quarter_weeks default = 13 each — but use each employee's own weeks_by_quarter (partial-quarter rule).
seniority_weekly bands (by years_of_service):
- 0–4 yrs → 0.00
- 5–9 → 48.00
- 10–14 → 82.00
- 15–19 → 126.00
- 20–24 → 170.00
- 25+ (max null) → 215.00
title_premium_pct: Assistant Principal 0.10, Associate Principal 0.10, Concertmaster 0.22, Principal 0.20, Section Lead 0.15, no title (null) → 0.
Per-employee per-quarter pay computation
For employee e, quarter Q with w = e.weeks_by_quarter[Q]:
- Minimum Weekly Scale = MWS × w
- Titled Position Premium =
title_premium_pct[title] × MWS × w … UNLESS e.combined_overscale_includes_title == true, in which case Titled Position Premium = 0 (the overscale already bundles the title premium per side letter).
- Seniority =
band_amount(e.years_of_service) × w
- Overscale =
e.overscale_weekly × w
Aggregations & output fields
roster_count = number of employees in the ensemble.
pay_types = the ordered list above.
quarter_totals[Q] = Σ over employees of (MWS + Title + Seniority + Overscale) for that quarter.
annual_pay_type_totals[pt] = Σ over employees & quarters of that pay type.
annual_total = Σ of all pay-type totals.
largest_pay_type = pay type with the largest annual total (enum).
combined_overscale_employee_count = count of employees with combined_overscale_includes_title == true.
partial_quarter_employee_count = count of employees with any weeks_by_quarter[Q] != 13.
Worked example (ENS-REDWOOD)
- roster_count
26; combined_overscale 0; partial_quarter 3 (employees with Q2=9, Q3=10, Q2=9).
- quarter_totals: Q1
977636.40, Q2 953292.40, Q3 969086.40, Q4 977636.40. (Q2/Q3 lower because of partial-quarter employees — each employee's own weeks drive the totals, not a flat 13.)
- annual_pay_type_totals: Minimum Weekly Scale
3379320.00, Titled Position Premium 297561.60, Seniority 142790.00, Overscale 57980.00; annual_total 3877651.60; largest = Minimum Weekly Scale.
- Sanity: MWS total / 2520 = total employee-weeks (REDWOOD = 1341 = 26×52 − 11 partial-week shortfall).
3. Family: Weekly payroll (train_003 — PAYROLL_REVIEW)
Inputs
request_memo: production_id. Fetch /api/payroll/productions?production_id=... (or the whole collection and index).
Rate book constants (CONFIRMED)
service_rates (per service, flat — except Rehearsal): Performance 260.25, Audit 260.25, 1hr Sound Check 80.00, 2hr Sound Check 142.50, Rehearsal 58.75 per hour with a 3-hour minimum call.
service_time_limits (hours, threshold for the over-time flag): Performance 3.0, Audit 3.0, Rehearsal 5.0, 1hr Sound Check 1.0, 2hr Sound Check 2.0.
premium_pct: concertmaster 0.20, principal_or_lead 0.15, quartet 0.15, electronic 0.25, first_double 0.25, additional_double 0.10, vacation 0.04.
weekly_guarantee = 2082.0.
conflict_thresholds: rehearsal_earliest_start 09:00, rehearsal_latest_end 18:30.
Service pay
- Performance / Audit / 1hr Sound Check / 2hr Sound Check: flat
service_rates[type] per assigned service (one payment per assigned service_id, regardless of duration; duration only feeds the flags).
- Rehearsal:
max(duration_hours, 3.0) × 58.75 — pay for the actual call length, floored at 3 hours. If duration exceeds the 5.0h time limit, still pay the actual duration (the breach only raises a flag; the time limit is NOT a pay cap).
Per-musician pay model (stacking order — CONFIRMED)
- base service pay = Σ assigned-service pay, split by category:
performance, audit, rehearsal, sound_check (sound_check = 1hr + 2hr Sound Check combined).
- premium category = Σ applicable role premiums × base (additive):
principal_or_lead 15% if principal==true OR lead==true (single 15%, not doubled if both).
quartet 15% if quartet==true.
concertmaster 20% if a concertmaster flag were set (none in current productions → 0).
- Electronic (electronic flag) handling: see substitute_adjustment below for electronic substitutes; a non-substitute electronic musician's 25% electronic premium goes in the
premium category.
- doubles category =
[0.25 + 0.10 × (doubles − 1)] × base if doubles ≥ 1, else 0. (doubles=1 → 25%; doubles=2 → 35%; doubles=3 → 45%.)
- vacation category =
0.04 × (base + premium + doubles) if vacation_eligible==true, else 0. (All substitutes observed have vacation_eligible==false, so substitutes get no vacation.)
- guarantee_adjustment =
max(0, 2082.0 − base service pay) for non-substitute musicians when their base service pay (service earnings only, BEFORE premiums/doubles) is below the weekly guarantee. Substitutes get NO guarantee. (Literal rule: "base service pay is below weekly_guarantee".)
- substitute_adjustment (when applicable): the electronic-substitute treatment. For a musician who is
electronic==true AND substitute==true, the 25% electronic premium is reported here as substitute_adjustment (it is NOT placed in the premium category). Non-electronic substitutes have substitute_adjustment = 0; the field is included only when a substitute (and especially an electronic substitute) is present. Substitutes otherwise still receive principal/lead/quartet premiums in the premium category and doubles in doubles.
- Decision point / uncertainty: this is the least-certain field. The "electronic substitute treatment" hints that the electronic premium for elec+sub musicians is split out. Alternative readings: (a) substitute_adjustment is always 0/omitted; (b) it is the forfeited guarantee (negative). The electronic-premium-split reading is adopted because it is the only one that yields a nonzero, rate-book-derived value consistent with the "electronic substitute treatment" hint. Re-examine against the specific test production's roster.
musician total
total = base + premium + doubles + vacation + guarantee_adjustment + substitute_adjustment. categories = object of nonzero category → 2dp amount.
category_totals
Sum each category across all musicians. Include substitute_adjustment only when nonzero (applicable). Omit zero-valued categories (the template marks some "when applicable").
service_counts
Object mapping each service type present in the schedule to its integer count (e.g. Performance 4, Audit 1, Rehearsal 2, 1hr Sound Check 1). Omit zero-count types.
conflict_flags (sorted alphabetically; enum)
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 service with duration_hours > service_time_limits[type] (strictly greater; equal-to-limit does NOT flag).
SOUND_CHECK_DURATION_MISMATCH — a sound-check service whose service_type nominal hours (1.0 for "1hr Sound Check", 2.0 for "2hr Sound Check") differ from duration_hours.
- Sort the resulting flag list alphabetically.
top_paid_musician_id
Musician with the highest total. Ties → lowest musician_id (ascending).
Output ordering
per_musician ordered by musician_id ascending. conflict_flags sorted alphabetically. Currency 2dp.
Worked example (PROD-HAMILTON-26)
- service_counts:
{"Rehearsal":2, "1hr Sound Check":1, "Performance":4, "Audit":1}.
- category_totals: performance
5205.00, audit 520.50, rehearsal 2467.50, sound_check 160.00, premium 1318.44, doubles 1405.10, vacation 378.00, guarantee_adjustment 1276.25, substitute_adjustment 325.31.
- weekly_total
13056.11.
- conflict_flags:
["REHEARSAL_EARLY_START","SERVICE_OVER_TIME_LIMIT"] (S01 rehearsal starts 08:45 < 09:00; S07 rehearsal 5.5h > 5.0h limit. No late end, no sound-check mismatch — S02 1hr Sound Check duration 1.0 matches.)
- per_musician (totals): M-H26-01 Avery
1951.88 (sub+elec; performance 1041.00, audit 260.25, doubles 325.31, substitute_adjustment 325.31), M-H26-02 Mira 2693.73, M-H26-03 Jon 3010.41, M-H26-04 Nadia 2406.94, M-H26-05 Theo 2993.14.
- top_paid_musician_id
M-H26-03 (Jon Reyes, 3010.41).
- Note on HAMILTON: every regular musician's base service pay < 2082, so all four regulars receive a guarantee_adjustment; the substitute (Avery) receives none.
4. Family: Regional dashboard (train_004 — REGIONAL_VIEW)
Inputs
request_memo: target_region_id, requested_comparison_years = [2024, 2025].
Step-by-step SOP
- Derive the region's branch list from
/api/finance/branches (canonical) — ascending sorted.
- For each year, sum across the region's branches and across that year's 12 periods (FY2024 = M1..M12; FY2025 = M13..M24):
revenue = Σ revenue accounts; sga = Σ sga accounts; allocations = Σ allocations accounts; ebitda = revenue − cogs − sga − allocations (cogs summed the same way even though it is not a displayed field).
- Worked (REG-WEST): FY2024 revenue
10453506.76, sga 3763931.94, allocations 354160.21, ebitda 2310369.33. FY2025 revenue 11139674.79, sga 3935915.21, allocations 376735.89, ebitda 2515012.15 (= Σ of the three branches' FY2025 ebitda, matching branch-close region_context).
- FY2025-only extra fields:
ebitda_margin = fy2025 ebitda / fy2025 revenue, 4dp → 0.2258.
sales_per_labor_headcount = fy2025 revenue / Σ(labor_headcount over region, M13..M24), currency 2dp → 24809.97 (region labor_hc = 449).
revenue_growth_pct = (fy2025 revenue − fy2024 revenue) / fy2024 revenue, 4dp → 0.0656.
top_ebitda_branch_id / bottom_ebitda_branch_id = branch in the region with max/min FY2025 ebitda. Worked: top BR-005, bottom BR-006.
region_reconciliation_variance (currency). Decision point — meaning is not fully pinned by the template. Adopted primary reading: the year-over-year ebitda dollar variance for the region = fy2025_ebitda − fy2024_ebitda → 204642.82. Plausible alternatives to consider if a test task's wording points otherwise: (a) revenue YoY dollar variance 686168.03; (b) the COGS "plug" that reconciles (revenue − sga − allocations) down to ebitda 4312011.54; (c) branch-sum vs region-total, which is 0 here (data is internally consistent — no stale region tags). Prefer the ebitda-YoY reading; re-evaluate from the task's management_focus if it singles out revenue or "tie-out".
- Output keys:
region_id, branch_ids, fy2024, fy2025, revenue_growth_pct, top_ebitda_branch_id, bottom_ebitda_branch_id, region_reconciliation_variance.
5. Family: Compensation forecast (train_005 — COMP_FORECAST)
Inputs
request_memo: ensemble_id, scenario_id, forecast_years = [current, year_plus_1, year_plus_2].
Scenario driver structure (CONFIRMED)
Each scenario (e.g. case_maple_board, case_redwood_baseline, case_cedar_negotiation, case_oak_sensitivity) has year_plus_1 and year_plus_2, each with:
mws_growth — fractional growth applied to MWS.
overscale_growth — fractional growth applied to each employee's overscale_weekly.
seniority_growth — fractional growth applied to the seniority band weekly amount.
title_pct_multiplier — multiplier applied to the title premium percentage.
Core rules (CONFIRMED)
- Cumulative / compound growth across forecast years. year_plus_1 applies its drivers to current; year_plus_2 applies its drivers ON TOP of year_plus_1.
MWS_y1 = MWS × (1 + y1.mws_growth); MWS_y2 = MWS_y1 × (1 + y2.mws_growth).
overscale_mult_y1 = (1 + y1.overscale_growth); overscale_mult_y2 = overscale_mult_y1 × (1 + y2.overscale_growth).
seniority_mult_y1 = (1 + y1.seniority_growth); seniority_mult_y2 = seniority_mult_y1 × (1 + y2.seniority_growth).
title_mult_y2 = y1.title_pct_multiplier × y2.title_pct_multiplier (multiplicative; for scenarios where one year is e.g. 0.98, the reduction compounds).
- Advance years of service before assigning seniority bands: year_plus_1 uses
years_of_service + 1; year_plus_2 uses years_of_service + 2. (This can move an employee up a band → large seniority growth — the dominant growth driver.)
- combined_overscale_includes_title persists: such employees still get NO separate Titled Position Premium in any forecast year; their (grown) overscale_weekly already bundles it.
- Partial quarters persist: use the same
weeks_by_quarter for all forecast years (do NOT normalize partial quarters to 13).
- Title premium in a forecast year =
title_pct × effective_title_mult × MWS_thatYear × weeks (the premium grows with MWS).
- Seniority in a forecast year =
band_amount(years + advance) × seniority_mult_thatYear × weeks.
- Overscale in a forecast year =
overscale_weekly × overscale_mult_thatYear × weeks.
Output fields
annual_totals: current (current-year total), year_plus_1, year_plus_2 — each = Σ all pay across all employees & quarters for that year.
growth_rates: year_plus_1_vs_current = (y1 − current)/current; year_plus_2_vs_year_plus_1 = (y2 − y1)/y1. Both 4dp.
year_plus_2_quarter_totals: Q1..Q4 for year_plus_2.
year_plus_2_pay_type_totals: the four pay types for year_plus_2.
largest_growth_pay_type: pay type with the largest growth rate from current → year_plus_2 (enum). (Alternative: largest absolute dollar growth — would usually be Minimum Weekly Scale. Prefer rate-based; "growth" mirrors the rate-based growth_rates field.)
combined_overscale_employee_count & partial_quarter_employee_count: same definitions as the current-year family (roster attributes, unchanged by forecasting).
Worked example (ENS-MAPLE, scenario case_maple_board)
Drivers: y1 {mws 0.035, overscale 0.012, seniority 0.018, title_mult 1.0}; y2 {mws 0.033, overscale 0.014, seniority 0.020, title_mult 1.0}.
- annual_totals: current
4232653.60, year_plus_1 4390313.79, year_plus_2 4545741.02.
- growth_rates: year_plus_1_vs_current
0.0372, year_plus_2_vs_year_plus_1 0.0354.
- year_plus_2_quarter_totals: Q1
1129891.58, Q2 1138616.48, Q3 1138616.48, Q4 1138616.48 (Q1 lower because ENS-MAPLE-005 has Q1=10 weeks — partial quarter persists).
- year_plus_2_pay_type_totals: Minimum Weekly Scale
3914775.18, Titled Position Premium 320833.74, Seniority 190829.80, Overscale 119302.29.
- largest_growth_pay_type
Seniority (rate ≈ 0.2331 current→year_plus_2, driven by both the seniority_growth multiplier and band advancement from +2 years; MWS ≈ 0.0692, Title ≈ 0.0692, Overscale ≈ 0.0262).
- combined_overscale_employee_count
4; partial_quarter_employee_count 1.
- Sanity: this ensemble has 4
combined_overscale_includes_title=true employees, so 4 employees contribute 0 to "Titled Position Premium" (their overscale covers it).
6. Common misjudgments & exclusion rules (DO / DON'T)
- DON'T sum
operating accounts (orders, revenue_units, active_customers, labor_headcount, admin_headcount, backlog) into revenue/cogs/sga/ebitda. They are metric_type: count. Use active_customers / labor_headcount only as DENOMINATORS for ARPU and sales-per-labor-headcount.
- DON'T compute regional ebitda as
revenue − sga − allocations (forgetting COGS). EBITDA always subtracts COGS even when the cogs field is hidden in the regional view.
- DO use each employee's own
weeks_by_quarter, not the flat quarter_weeks: 13, for compensation totals — partial-quarter employees materially lower the affected quarter.
- DO suppress the Titled Position Premium when
combined_overscale_includes_title == true (both current-year and forecast).
- DO advance years of service (+1, +2) in the forecast before picking the seniority band — this is the single biggest driver of seniority growth.
- DO apply scenario drivers cumulatively (compound) across year_plus_1 → year_plus_2.
- DON'T cap rehearsal pay at the 5.0h time limit. Pay the actual call (floored at 3h); the >5.0h breach only raises a
SERVICE_OVER_TIME_LIMIT flag.
- DON'T give substitutes the weekly guarantee (only "guaranteed regular players" = non-substitutes get it). All observed substitutes also have
vacation_eligible=false.
- DO use
>=-style strictly-greater for the over-time flag: equal-to-limit does NOT flag; only duration_hours > limit does.
- DO rank descending (rank 1 = highest/best) for all
*_rank_desc fields.
- DO derive region membership from
/api/finance/branches and ignore stale memo notes claiming a different region for a branch.
- DO round currency to 2dp and decimal-percent/ratio to 4dp, per each field's
field_types label.
- DO sort
conflict_flags alphabetically and per_musician by musician_id.
7. Quick numeric reference (confirmed from live env)
Compensation rate book — MWS 2520.0; seniority weekly [0→0, 5→48, 10→82, 15→126, 20→170, 25→215]; title pct {Concertmaster 0.22, Principal 0.20, Section Lead 0.15, Assistant/Associate Principal 0.10}; current_year 2026; quarter_weeks 13/13/13/13 (overridden by roster).
Payroll rate book — Performance/Audit 260.25; Rehearsal 58.75/hr (3hr min); 1hr Sound Check 80.00; 2hr Sound Check 142.50. Limits 3/3/5/1/2 hrs. Premiums: concertmaster 20%, principal_or_lead 15%, quartet 15%, electronic 25%, first_double 25%, additional_double 10%, vacation 4%. weekly_guarantee 2082.0. Rehearsal window 09:00–18:30.
Finance — 12 branches, 4 regions (North: BR-001/002/003; West: BR-004/005/006; East: BR-007/008/011; South: BR-009/010/012). M1–M12 = FY2024, M13–M24 = FY2025. IS = revenue − cogs(sga,alloc) → ebitda. ARPU ÷ active_customers; sales/labor ÷ labor_headcount.
Scenarios — case_cedar_negotiation, case_maple_board, case_oak_sensitivity, case_redwood_baseline. Each has year_plus_1/year_plus_2 with mws_growth, overscale_growth, seniority_growth, title_pct_multiplier.
8. Open decision points (re-evaluate per test task wording)
sales_growth_rank_desc period — annual (FY25 vs FY24) preferred; MoM (M24 vs M23) is the alternative. Worked annual: BR-004 rank 2, top BR-002. Worked MoM: BR-004 rank 11, top BR-002.
region_reconciliation_variance — ebitda YoY (204642.82) preferred; alternatives revenue-YoY (686168.03) or COGS plug (4312011.54).
largest_growth_pay_type — growth-rate based (Seniority) preferred; absolute-dollar alternative (Minimum Weekly Scale).
substitute_adjustment — electronic-premium-split for elec+sub musicians preferred; the field is the least-certain in the payroll model.
current_month/prior_month label format — M-code (M24) preferred; Dec 2025 style acceptable.
- ARPU / sales_per_labor_headcount precision — 2dp (per
currency label) preferred; 4dp is the alternative if the evaluator treats them as ratios.
1---2name: self-attempt-03-133description: Crescent Finance Ops — Solver Skill (task_group_009)4---5# Crescent Finance Ops — Solver Skill (task_group_009)67A reusable SOP for the five task families in this group: **branch close**, **compensation current-year**, **weekly payroll**, **regional dashboard**, **compensation forecast**. All rules below were confirmed by fetching the live remote environment and computing the train tasks end-to-end. Worked numbers from the train tasks are included ONLY as illustrations of each rule (they are not to be copied verbatim for test tasks — recompute against the live data for any new target id).89---1011## 0. Environment & shared conventions1213### Base URL (LIVE remote — always use this)14`<remote-env-url>`15The staged `payloads/environment_access.json` already carries this remote `base_url`. NEVER use `127.0.0.1` / a local dev server — it is not running. Fetch with `curl -s $BASE/<endpoint>`; all responses are JSON.1617### Endpoint catalog18| Endpoint | Returns | Used by |19|---|---|---|20| `GET /health` | `{status:ok}` sanity check | all |21| `GET /api/manifest` | entity lists + record_counts + seed | discovery |22| `GET /api/finance/branches` | 12 branches w/ canonical `branch_id`→`region_id`→`region_name` | finance families |23| `GET /api/finance/period-map` | 24 monthly periods M1..M24 → fiscal_year/month_name/month_number | finance families |24| `GET /api/finance/accounts` | 14 accounts w/ `category` + `metric_type` | finance families |25| `GET /api/finance/records` | 168 rows = 14 accounts × 12 branches; each row = `{account, branch_id, branch_name, region_id, values:{M1..M24}}` | finance families |26| `GET /api/compensation/rate-book` | MWS, pay_types, quarter_weeks, seniority_weekly, title_premium_pct, business_rules, `current_year`=2026 | comp families |27| `GET /api/compensation/rosters` | 109 employees across 4 ensembles; per-employee `title, years_of_service, overscale_weekly, combined_overscale_includes_title, weeks_by_quarter` | comp families |28| `GET /api/compensation/scenarios` | 4 scenarios; each has `year_plus_1`/`year_plus_2` drivers | forecast |29| `GET /api/payroll/rate-book` | service_rates, service_time_limits, premium_pct, weekly_guarantee, conflict_thresholds, business_rules | payroll |30| `GET /api/payroll/productions` | 17 productions; each = `{production_id, title, week_start, roster[], schedule[]}` | payroll |3132Useful query filters: `?branch_id=BR-009`, `?region=REG-SOUTH`, `?account=product_revenue`, `?ensemble_id=ENS-REDWOOD`, `?production_id=PROD-HAMILTON-26`. (It is usually simpler to fetch the whole collection once and index it in Python.)3334### Rounding rules (follow the `field_types` label in each answer_template)35- **currency** → 2 decimals.36- **decimal percent / ratio** → 4 decimals, expressed as a decimal fraction (e.g. `0.0138`, NOT `1.38`).37- **integer** counts → exact integer.38- Lists: ascending stable IDs unless a rank field says otherwise; `conflict_flags` sorted alphabetically; `per_musician` ordered by `musician_id`.39- ARPU and sales_per_labor_headcount are labeled `currency` in the templates → round to **2 decimals** (even though they are computed as ratios). `ebitda_margin`, `revenue_growth_pct`, `ebitda_growth_pct`, MoM `pct`, forecast `growth_rates` are `decimal percent` → **4 decimals**.4041### Period → fiscal-year convention (CONFIRMED from period-map)42- `M1..M12` → **FY2024** (Jan..Dec 2024).43- `M13..M24` → **FY2025** (Jan..Dec 2025).44- `M23` = Nov 2025, `M24` = Dec 2025. A fiscal year = 12 consecutive M-periods.45- For `period_convention`: `M1_to_M12` = `"FY2024"`, `M13_to_M24` = `"FY2025"`. `current_month`/`prior_month` are period-label strings — use the M-code of the close/prior period (e.g. `"M24"`, `"M23"`); a `"Dec 2025"`/`"Nov 2025"` style is an acceptable alternative if the template expects a human label.4647### Account categories (CONFIRMED from /api/finance/accounts)48- **revenue**: `product_revenue`, `service_revenue`49- **cogs**: `direct_materials_cogs`, `direct_labor_cogs`50- **sga**: `sales_sga`, `admin_sga`, `occupancy_sga`51- **allocations**: `shared_service_allocations`52- **operating** (metric_type = `count`, NOT currency — never sum these into revenue/ebitda): `orders`, `revenue_units`, `active_customers`, `labor_headcount`, `admin_headcount`, `backlog`5354### Income-statement construction (CONFIRMED)55- `revenue` = Σ revenue accounts56- `cogs` = Σ cogs accounts57- `gross_margin` = revenue − cogs58- `sga` = Σ sga accounts59- `allocations` = Σ allocations accounts60- `ebitda` = revenue − cogs − sga − allocations (= gross_margin − sga − allocations). EBITDA always subtracts COGS, **even in regional views where the cogs/gross_margin fields are not displayed**.61- `ebitda_margin` = ebitda / revenue62- `arpu` = revenue / (Σ `active_customers` over the same periods)63- `sales_per_labor_headcount` = revenue / (Σ `labor_headcount` over the same periods)64 - operating counts are stored as period values just like currency accounts; sum them across the same periods used for revenue.6566### Region membership (CONFIRMED from /api/finance/branches — canonical source of truth)67- REG-NORTH: BR-001, BR-002, BR-00368- REG-WEST: BR-004, BR-005, BR-00669- REG-EAST: BR-007, BR-008, BR-01170- REG-SOUTH: BR-009, BR-010, BR-01271- **Always derive region membership from `/api/finance/branches`.** Ignore any stale/draft "background notes" or claimed region in a request memo. Do NOT exclude a branch from its region because a memo says otherwise. The active operations data is authoritative.72- `branch_ids` lists are ascending-sorted.73- `ebitda_rank_desc` / `*_rank_desc` = descending rank (1 = best/highest). For region ebitda rank, rank the target branch among its region's branches by FY2025 ebitda descending.7475### Dataset facts (seed 9009)76- 12 branches, 4 regions, 168 finance records (14 accounts × 12 branches; each row carries all 24 M-periods).77- 4 ensembles: ENS-CEDAR (31), ENS-MAPLE (28), ENS-OAK (24), ENS-REDWOOD (26) — 109 roster rows total.78- 17 productions. Payroll roster flags available: `lead, principal, quartet, electronic, doubles, substitute, vacation_eligible` (NO `concertmaster` or `guaranteed` flag on productions — the concertmaster premium is defined in the rate book but no production roster sets it, so it is $0 in payroll).7980---8182## 1. Family: Branch close (train_001 — `BR_CLOSE`)8384### Inputs85`request_memo`: `target_branch_id`, `close_period` (e.g. M24), `prior_period` (e.g. M23). Reporting focus: income statement, revenue variance, current-FY metrics, regional context, branch rankings.8687### Step-by-step SOP881. Fetch `/api/finance/branches`, `/api/finance/period-map`, `/api/finance/accounts`, `/api/finance/records`. Index records by `(branch_id, account)` → `values[M]`.892. **`target_branch_id` / `target_branch_name`**: from branches list. `period_convention`: M1-M12→FY2024, M13-M24→FY2025; `current_month`=close_period code, `prior_month`=prior_period code.903. **`m24_income_statement`** (single-period IS for close_period): sum each account category over `[close_period]` only.91 - Worked (BR-004, M24): revenue `293306.29`, cogs `108946.99`, gross_margin `184359.30`, sga `100245.35`, allocations `9280.16`, ebitda `74833.79`.924. **`mom_revenue_variance`**: `amount` = revenue(close_period) − revenue(prior_period); `pct` = amount / revenue(prior_period), 4dp.93 - Worked (BR-004): M23 revenue `289310.14`, amount `3996.15`, pct `0.0138`.945. **`fy2025_vs_fy2024`**: 95 - FY2024 = sum of M1..M12; FY2025 = sum of M13..M24 (for every category).96 - `fy2025` block: revenue, cogs, gross_margin, sga, allocations, ebitda, `ebitda_margin` (=fy2025 ebitda/fy2025 revenue, 4dp), `arpu` (=fy2025 revenue / Σ active_customers M13..M24, currency 2dp), `sales_per_labor_headcount` (=fy2025 revenue / Σ labor_headcount M13..M24, currency 2dp).97 - Worked (BR-004): FY2024 revenue `3177038.53` ebitda `749925.74`; FY2025 revenue `3483871.60` ebitda `856610.10` ebitda_margin `0.2459` arpu `999.39` (active_customers=3486) sales_per_labor_hc `24534.31` (labor_hc=142).98 - `revenue_growth_pct` = (fy2025_rev − fy2024_rev)/fy2024_rev, 4dp → `0.0966`.99 - `ebitda_growth_pct` = (fy2025_ebitda − fy2024_ebitda)/fy2024_ebitda, 4dp → `0.1423`.1006. **`region_context`**: target branch's region from canonical map. `branch_ids` = ascending list of that region's branches. `fy2025_ebitda` = Σ FY2025 ebitda across region branches. `ebitda_rank_desc` = rank of target branch's FY2025 ebitda within the region, descending (1=best).101 - Worked (BR-004, REG-WEST): branch_ids `[BR-004,BR-005,BR-006]`, fy2025_ebitda `2515012.15`, ebitda_rank_desc `2` (BR-005=1, BR-004=2, BR-006=3).1027. **`branch_rankings`**: compute across ALL 12 branches.103 - `sales_growth_rank_desc` = descending rank of target branch's **annual revenue growth** (FY2025 vs FY2024). `top_sales_growth_branch_id` = branch with max annual revenue growth. `top_arpu_branch_id` = branch with max FY2025 ARPU.104 - Worked (annual): BR-004 rank `2`, top_sales_growth `BR-002`, top_arpu `BR-012`.105 - **Decision point — period for "sales growth":** primary interpretation is annual (FY25 vs FY24), consistent with `top_arpu` using FY2025. If a test task's wording emphasizes the monthly close/MoM, the alternative is MoM (M24 vs M23) revenue growth — under that reading BR-004 ranks 11 and top is still BR-002. Prefer annual unless the task explicitly says "month-over-month".1068. Round: currency 2dp, percents/ratios 4dp. Output keys exactly per template.107108---109110## 2. Family: Compensation current-year (train_002 — `COMP_CURRENT`)111112### Inputs113`request_memo`: `ensemble_id`, `summary_type` = `current_year_by_quarter_and_pay_type`.114115### Rate book constants (CONFIRMED, current_year = 2026)116- `minimum_weekly_scale` (MWS) = **2520.0** /week.117- `pay_types` (use this order): `["Minimum Weekly Scale", "Titled Position Premium", "Seniority", "Overscale"]`.118- `quarter_weeks` default = 13 each — but **use each employee's own `weeks_by_quarter`** (partial-quarter rule).119- `seniority_weekly` bands (by `years_of_service`):120 - 0–4 yrs → 0.00121 - 5–9 → 48.00122 - 10–14 → 82.00123 - 15–19 → 126.00124 - 20–24 → 170.00125 - 25+ (max null) → 215.00126- `title_premium_pct`: Assistant Principal 0.10, Associate Principal 0.10, Concertmaster 0.22, Principal 0.20, Section Lead 0.15, no title (null) → 0.127128### Per-employee per-quarter pay computation129For employee `e`, quarter `Q` with `w = e.weeks_by_quarter[Q]`:130- **Minimum Weekly Scale** = MWS × w131- **Titled Position Premium** = `title_premium_pct[title] × MWS × w` … **UNLESS** `e.combined_overscale_includes_title == true`, in which case Titled Position Premium = 0 (the overscale already bundles the title premium per side letter).132- **Seniority** = `band_amount(e.years_of_service) × w`133- **Overscale** = `e.overscale_weekly × w`134135### Aggregations & output fields136- `roster_count` = number of employees in the ensemble.137- `pay_types` = the ordered list above.138- `quarter_totals[Q]` = Σ over employees of (MWS + Title + Seniority + Overscale) for that quarter.139- `annual_pay_type_totals[pt]` = Σ over employees & quarters of that pay type.140- `annual_total` = Σ of all pay-type totals.141- `largest_pay_type` = pay type with the largest annual total (enum).142- `combined_overscale_employee_count` = count of employees with `combined_overscale_includes_title == true`.143- `partial_quarter_employee_count` = count of employees with any `weeks_by_quarter[Q] != 13`.144145### Worked example (ENS-REDWOOD)146- roster_count `26`; combined_overscale `0`; partial_quarter `3` (employees with Q2=9, Q3=10, Q2=9).147- quarter_totals: Q1 `977636.40`, Q2 `953292.40`, Q3 `969086.40`, Q4 `977636.40`. (Q2/Q3 lower because of partial-quarter employees — each employee's own weeks drive the totals, not a flat 13.)148- annual_pay_type_totals: Minimum Weekly Scale `3379320.00`, Titled Position Premium `297561.60`, Seniority `142790.00`, Overscale `57980.00`; annual_total `3877651.60`; largest = `Minimum Weekly Scale`.149- Sanity: MWS total / 2520 = total employee-weeks (REDWOOD = 1341 = 26×52 − 11 partial-week shortfall).150151---152153## 3. Family: Weekly payroll (train_003 — `PAYROLL_REVIEW`)154155### Inputs156`request_memo`: `production_id`. Fetch `/api/payroll/productions?production_id=...` (or the whole collection and index).157158### Rate book constants (CONFIRMED)159- `service_rates` (per service, flat — except Rehearsal): Performance `260.25`, Audit `260.25`, 1hr Sound Check `80.00`, 2hr Sound Check `142.50`, Rehearsal `58.75` **per hour with a 3-hour minimum call**.160- `service_time_limits` (hours, threshold for the over-time flag): Performance 3.0, Audit 3.0, Rehearsal 5.0, 1hr Sound Check 1.0, 2hr Sound Check 2.0.161- `premium_pct`: concertmaster 0.20, principal_or_lead 0.15, quartet 0.15, electronic 0.25, first_double 0.25, additional_double 0.10, vacation 0.04.162- `weekly_guarantee` = `2082.0`.163- `conflict_thresholds`: rehearsal_earliest_start `09:00`, rehearsal_latest_end `18:30`.164165### Service pay166- Performance / Audit / 1hr Sound Check / 2hr Sound Check: flat `service_rates[type]` per assigned service (one payment per assigned service_id, regardless of duration; duration only feeds the flags).167- Rehearsal: `max(duration_hours, 3.0) × 58.75` — pay for the actual call length, floored at 3 hours. If duration exceeds the 5.0h time limit, still pay the actual duration (the breach only raises a flag; the time limit is NOT a pay cap).168169### Per-musician pay model (stacking order — CONFIRMED)1701. **base service pay** = Σ assigned-service pay, split by category:171 - `performance`, `audit`, `rehearsal`, `sound_check` (sound_check = 1hr + 2hr Sound Check combined).1722. **premium** category = Σ applicable role premiums × base (additive):173 - `principal_or_lead` 15% if `principal==true` OR `lead==true` (single 15%, not doubled if both).174 - `quartet` 15% if `quartet==true`.175 - `concertmaster` 20% if a concertmaster flag were set (none in current productions → 0).176 - Electronic (electronic flag) handling: see substitute_adjustment below for electronic substitutes; a non-substitute electronic musician's 25% electronic premium goes in the `premium` category.1773. **doubles** category = `[0.25 + 0.10 × (doubles − 1)] × base` if `doubles ≥ 1`, else 0. (doubles=1 → 25%; doubles=2 → 35%; doubles=3 → 45%.)1784. **vacation** category = `0.04 × (base + premium + doubles)` if `vacation_eligible==true`, else 0. (All substitutes observed have `vacation_eligible==false`, so substitutes get no vacation.)1795. **guarantee_adjustment** = `max(0, 2082.0 − base service pay)` for **non-substitute** musicians when their base service pay (service earnings only, BEFORE premiums/doubles) is below the weekly guarantee. Substitutes get NO guarantee. (Literal rule: "base service pay is below weekly_guarantee".)1806. **substitute_adjustment** (when applicable): the **electronic-substitute treatment**. For a musician who is `electronic==true AND substitute==true`, the 25% electronic premium is reported here as `substitute_adjustment` (it is NOT placed in the `premium` category). Non-electronic substitutes have `substitute_adjustment = 0`; the field is included only when a substitute (and especially an electronic substitute) is present. Substitutes otherwise still receive principal/lead/quartet premiums in the `premium` category and doubles in `doubles`.181 - **Decision point / uncertainty:** this is the least-certain field. The "electronic substitute treatment" hints that the electronic premium for elec+sub musicians is split out. Alternative readings: (a) substitute_adjustment is always 0/omitted; (b) it is the forfeited guarantee (negative). The electronic-premium-split reading is adopted because it is the only one that yields a nonzero, rate-book-derived value consistent with the "electronic substitute treatment" hint. Re-examine against the specific test production's roster.182183### musician total184`total` = base + premium + doubles + vacation + guarantee_adjustment + substitute_adjustment. `categories` = object of **nonzero** category → 2dp amount.185186### category_totals187Sum each category across all musicians. Include `substitute_adjustment` only when nonzero (applicable). Omit zero-valued categories (the template marks some "when applicable").188189### service_counts190Object mapping each service type present in the schedule to its integer count (e.g. Performance 4, Audit 1, Rehearsal 2, 1hr Sound Check 1). Omit zero-count types.191192### conflict_flags (sorted alphabetically; enum)193- `REHEARSAL_EARLY_START` — any rehearsal with `start_time < "09:00"`.194- `REHEARSAL_LATE_END` — any rehearsal with `end_time > "18:30"`.195- `SERVICE_OVER_TIME_LIMIT` — any service with `duration_hours > service_time_limits[type]` (strictly greater; equal-to-limit does NOT flag).196- `SOUND_CHECK_DURATION_MISMATCH` — a sound-check service whose `service_type` nominal hours (1.0 for "1hr Sound Check", 2.0 for "2hr Sound Check") differ from `duration_hours`.197- Sort the resulting flag list alphabetically.198199### top_paid_musician_id200Musician with the highest `total`. Ties → lowest `musician_id` (ascending).201202### Output ordering203`per_musician` ordered by `musician_id` ascending. `conflict_flags` sorted alphabetically. Currency 2dp.204205### Worked example (PROD-HAMILTON-26)206- service_counts: `{"Rehearsal":2, "1hr Sound Check":1, "Performance":4, "Audit":1}`.207- category_totals: performance `5205.00`, audit `520.50`, rehearsal `2467.50`, sound_check `160.00`, premium `1318.44`, doubles `1405.10`, vacation `378.00`, guarantee_adjustment `1276.25`, substitute_adjustment `325.31`.208- weekly_total `13056.11`.209- conflict_flags: `["REHEARSAL_EARLY_START","SERVICE_OVER_TIME_LIMIT"]` (S01 rehearsal starts 08:45 < 09:00; S07 rehearsal 5.5h > 5.0h limit. No late end, no sound-check mismatch — S02 1hr Sound Check duration 1.0 matches.)210- per_musician (totals): M-H26-01 Avery `1951.88` (sub+elec; performance 1041.00, audit 260.25, doubles 325.31, substitute_adjustment 325.31), M-H26-02 Mira `2693.73`, M-H26-03 Jon `3010.41`, M-H26-04 Nadia `2406.94`, M-H26-05 Theo `2993.14`.211- top_paid_musician_id `M-H26-03` (Jon Reyes, 3010.41).212- Note on HAMILTON: every regular musician's base service pay < 2082, so all four regulars receive a guarantee_adjustment; the substitute (Avery) receives none.213214---215216## 4. Family: Regional dashboard (train_004 — `REGIONAL_VIEW`)217218### Inputs219`request_memo`: `target_region_id`, `requested_comparison_years` = [2024, 2025].220221### Step-by-step SOP2221. Derive the region's branch list from `/api/finance/branches` (canonical) — ascending sorted.2232. For each year, sum across the region's branches and across that year's 12 periods (FY2024 = M1..M12; FY2025 = M13..M24):224 - `revenue` = Σ revenue accounts; `sga` = Σ sga accounts; `allocations` = Σ allocations accounts; `ebitda` = revenue − cogs − sga − allocations (cogs summed the same way even though it is not a displayed field).225 - Worked (REG-WEST): FY2024 revenue `10453506.76`, sga `3763931.94`, allocations `354160.21`, ebitda `2310369.33`. FY2025 revenue `11139674.79`, sga `3935915.21`, allocations `376735.89`, ebitda `2515012.15` (= Σ of the three branches' FY2025 ebitda, matching branch-close region_context).2263. FY2025-only extra fields:227 - `ebitda_margin` = fy2025 ebitda / fy2025 revenue, 4dp → `0.2258`.228 - `sales_per_labor_headcount` = fy2025 revenue / Σ(labor_headcount over region, M13..M24), currency 2dp → `24809.97` (region labor_hc = 449).2294. `revenue_growth_pct` = (fy2025 revenue − fy2024 revenue) / fy2024 revenue, 4dp → `0.0656`.2305. `top_ebitda_branch_id` / `bottom_ebitda_branch_id` = branch in the region with max/min FY2025 ebitda. Worked: top `BR-005`, bottom `BR-006`.2316. `region_reconciliation_variance` (currency). **Decision point — meaning is not fully pinned by the template.** Adopted primary reading: the year-over-year ebitda dollar variance for the region = fy2025_ebitda − fy2024_ebitda → `204642.82`. Plausible alternatives to consider if a test task's wording points otherwise: (a) revenue YoY dollar variance `686168.03`; (b) the COGS "plug" that reconciles (revenue − sga − allocations) down to ebitda `4312011.54`; (c) branch-sum vs region-total, which is 0 here (data is internally consistent — no stale region tags). Prefer the ebitda-YoY reading; re-evaluate from the task's `management_focus` if it singles out revenue or "tie-out".2327. Output keys: `region_id`, `branch_ids`, `fy2024`, `fy2025`, `revenue_growth_pct`, `top_ebitda_branch_id`, `bottom_ebitda_branch_id`, `region_reconciliation_variance`.233234---235236## 5. Family: Compensation forecast (train_005 — `COMP_FORECAST`)237238### Inputs239`request_memo`: `ensemble_id`, `scenario_id`, `forecast_years` = [current, year_plus_1, year_plus_2].240241### Scenario driver structure (CONFIRMED)242Each scenario (e.g. `case_maple_board`, `case_redwood_baseline`, `case_cedar_negotiation`, `case_oak_sensitivity`) has `year_plus_1` and `year_plus_2`, each with:243- `mws_growth` — fractional growth applied to MWS.244- `overscale_growth` — fractional growth applied to each employee's `overscale_weekly`.245- `seniority_growth` — fractional growth applied to the seniority band weekly amount.246- `title_pct_multiplier` — multiplier applied to the title premium percentage.247248### Core rules (CONFIRMED)249- **Cumulative / compound growth across forecast years.** year_plus_1 applies its drivers to current; year_plus_2 applies its drivers ON TOP of year_plus_1.250 - `MWS_y1 = MWS × (1 + y1.mws_growth)`; `MWS_y2 = MWS_y1 × (1 + y2.mws_growth)`.251 - `overscale_mult_y1 = (1 + y1.overscale_growth)`; `overscale_mult_y2 = overscale_mult_y1 × (1 + y2.overscale_growth)`.252 - `seniority_mult_y1 = (1 + y1.seniority_growth)`; `seniority_mult_y2 = seniority_mult_y1 × (1 + y2.seniority_growth)`.253 - `title_mult_y2 = y1.title_pct_multiplier × y2.title_pct_multiplier` (multiplicative; for scenarios where one year is e.g. 0.98, the reduction compounds).254- **Advance years of service** before assigning seniority bands: year_plus_1 uses `years_of_service + 1`; year_plus_2 uses `years_of_service + 2`. (This can move an employee up a band → large seniority growth — the dominant growth driver.)255- **combined_overscale_includes_title persists**: such employees still get NO separate Titled Position Premium in any forecast year; their (grown) overscale_weekly already bundles it.256- **Partial quarters persist**: use the same `weeks_by_quarter` for all forecast years (do NOT normalize partial quarters to 13).257- Title premium in a forecast year = `title_pct × effective_title_mult × MWS_thatYear × weeks` (the premium grows with MWS).258- Seniority in a forecast year = `band_amount(years + advance) × seniority_mult_thatYear × weeks`.259- Overscale in a forecast year = `overscale_weekly × overscale_mult_thatYear × weeks`.260261### Output fields262- `annual_totals`: `current` (current-year total), `year_plus_1`, `year_plus_2` — each = Σ all pay across all employees & quarters for that year.263- `growth_rates`: `year_plus_1_vs_current` = (y1 − current)/current; `year_plus_2_vs_year_plus_1` = (y2 − y1)/y1. Both 4dp.264- `year_plus_2_quarter_totals`: Q1..Q4 for year_plus_2.265- `year_plus_2_pay_type_totals`: the four pay types for year_plus_2.266- `largest_growth_pay_type`: pay type with the largest **growth rate** from current → year_plus_2 (enum). (Alternative: largest absolute dollar growth — would usually be Minimum Weekly Scale. Prefer rate-based; "growth" mirrors the rate-based `growth_rates` field.)267- `combined_overscale_employee_count` & `partial_quarter_employee_count`: same definitions as the current-year family (roster attributes, unchanged by forecasting).268269### Worked example (ENS-MAPLE, scenario `case_maple_board`)270Drivers: y1 {mws 0.035, overscale 0.012, seniority 0.018, title_mult 1.0}; y2 {mws 0.033, overscale 0.014, seniority 0.020, title_mult 1.0}.271- annual_totals: current `4232653.60`, year_plus_1 `4390313.79`, year_plus_2 `4545741.02`.272- growth_rates: year_plus_1_vs_current `0.0372`, year_plus_2_vs_year_plus_1 `0.0354`.273- year_plus_2_quarter_totals: Q1 `1129891.58`, Q2 `1138616.48`, Q3 `1138616.48`, Q4 `1138616.48` (Q1 lower because ENS-MAPLE-005 has Q1=10 weeks — partial quarter persists).274- year_plus_2_pay_type_totals: Minimum Weekly Scale `3914775.18`, Titled Position Premium `320833.74`, Seniority `190829.80`, Overscale `119302.29`.275- largest_growth_pay_type `Seniority` (rate ≈ 0.2331 current→year_plus_2, driven by both the seniority_growth multiplier and band advancement from +2 years; MWS ≈ 0.0692, Title ≈ 0.0692, Overscale ≈ 0.0262).276- combined_overscale_employee_count `4`; partial_quarter_employee_count `1`.277- Sanity: this ensemble has 4 `combined_overscale_includes_title=true` employees, so 4 employees contribute 0 to "Titled Position Premium" (their overscale covers it).278279---280281## 6. Common misjudgments & exclusion rules (DO / DON'T)282283- **DON'T** sum `operating` accounts (orders, revenue_units, active_customers, labor_headcount, admin_headcount, backlog) into revenue/cogs/sga/ebitda. They are `metric_type: count`. Use active_customers / labor_headcount only as DENOMINATORS for ARPU and sales-per-labor-headcount.284- **DON'T** compute regional ebitda as `revenue − sga − allocations` (forgetting COGS). EBITDA always subtracts COGS even when the cogs field is hidden in the regional view.285- **DO** use each employee's own `weeks_by_quarter`, not the flat `quarter_weeks: 13`, for compensation totals — partial-quarter employees materially lower the affected quarter.286- **DO** suppress the Titled Position Premium when `combined_overscale_includes_title == true` (both current-year and forecast).287- **DO** advance years of service (+1, +2) in the forecast before picking the seniority band — this is the single biggest driver of seniority growth.288- **DO** apply scenario drivers cumulatively (compound) across year_plus_1 → year_plus_2.289- **DON'T** cap rehearsal pay at the 5.0h time limit. Pay the actual call (floored at 3h); the >5.0h breach only raises a `SERVICE_OVER_TIME_LIMIT` flag.290- **DON'T** give substitutes the weekly guarantee (only "guaranteed regular players" = non-substitutes get it). All observed substitutes also have `vacation_eligible=false`.291- **DO** use `>=`-style strictly-greater for the over-time flag: equal-to-limit does NOT flag; only `duration_hours > limit` does.292- **DO** rank descending (rank 1 = highest/best) for all `*_rank_desc` fields.293- **DO** derive region membership from `/api/finance/branches` and ignore stale memo notes claiming a different region for a branch.294- **DO** round currency to 2dp and decimal-percent/ratio to 4dp, per each field's `field_types` label.295- **DO** sort `conflict_flags` alphabetically and `per_musician` by `musician_id`.296297---298299## 7. Quick numeric reference (confirmed from live env)300301**Compensation rate book** — MWS 2520.0; seniority weekly [0→0, 5→48, 10→82, 15→126, 20→170, 25→215]; title pct {Concertmaster 0.22, Principal 0.20, Section Lead 0.15, Assistant/Associate Principal 0.10}; current_year 2026; quarter_weeks 13/13/13/13 (overridden by roster).302303**Payroll rate book** — Performance/Audit 260.25; Rehearsal 58.75/hr (3hr min); 1hr Sound Check 80.00; 2hr Sound Check 142.50. Limits 3/3/5/1/2 hrs. Premiums: concertmaster 20%, principal_or_lead 15%, quartet 15%, electronic 25%, first_double 25%, additional_double 10%, vacation 4%. weekly_guarantee 2082.0. Rehearsal window 09:00–18:30.304305**Finance** — 12 branches, 4 regions (North: BR-001/002/003; West: BR-004/005/006; East: BR-007/008/011; South: BR-009/010/012). M1–M12 = FY2024, M13–M24 = FY2025. IS = revenue − cogs(sga,alloc) → ebitda. ARPU ÷ active_customers; sales/labor ÷ labor_headcount.306307**Scenarios** — `case_cedar_negotiation`, `case_maple_board`, `case_oak_sensitivity`, `case_redwood_baseline`. Each has year_plus_1/year_plus_2 with `mws_growth`, `overscale_growth`, `seniority_growth`, `title_pct_multiplier`.308309---310311## 8. Open decision points (re-evaluate per test task wording)3121. **`sales_growth_rank_desc` period** — annual (FY25 vs FY24) preferred; MoM (M24 vs M23) is the alternative. Worked annual: BR-004 rank 2, top BR-002. Worked MoM: BR-004 rank 11, top BR-002.3132. **`region_reconciliation_variance`** — ebitda YoY (204642.82) preferred; alternatives revenue-YoY (686168.03) or COGS plug (4312011.54).3143. **`largest_growth_pay_type`** — growth-rate based (Seniority) preferred; absolute-dollar alternative (Minimum Weekly Scale).3154. **`substitute_adjustment`** — electronic-premium-split for elec+sub musicians preferred; the field is the least-certain in the payroll model.3165. **`current_month`/`prior_month` label format** — M-code (`M24`) preferred; `Dec 2025` style acceptable.3176. **ARPU / sales_per_labor_headcount precision** — 2dp (per `currency` label) preferred; 4dp is the alternative if the evaluator treats them as ratios.