Crescent Finance Ops Reporting SOP
This skill covers three task families served by one read-only HTTP API:
- Finance — branch monthly-close packages and regional management views (income statement, growth, ratios, rankings).
- Compensation — ensemble (orchestra) current-year summaries and multi-year board forecasts from rate books, rosters, and scenarios.
- Payroll — weekly touring-production payroll control (per-service pay, premiums, vacation, guarantees, CBA conflict flags).
Each task ships three payload files. Read all three before computing:
prompt.txt — domain + what to produce.
payloads/request_memo.json — the target entity id (branch/region/ensemble/production), periods/years, scenario id, and a *_focus list of what to report. memo_note text is flavor/distractor — ignore unrelated names (e.g. "Harbor North draft workbook"); always reconcile against the live API data for the requested target id.
payloads/answer_template.json — the authoritative contract: required_top_level_keys (return exactly these keys), field_types (exact nested key names + shapes), and a description line with rounding/ordering rules. Mirror its key names and nesting verbatim.
1. Remote API access
- Base URL:
<remote-env-url> (ignore any base_url inside payloads such as 127.0.0.1:* — they are wrong).
- All reads are HTTP GET. Fetch with
curl -s "<base>/<endpoint>".
- Endpoints by domain:
- Finance:
/api/finance/branches, /api/finance/period-map, /api/finance/accounts, /api/finance/records (filters ?branch_id= ?region= ?account=).
- Compensation:
/api/compensation/rate-book, /api/compensation/rosters?ensemble_id=, /api/compensation/scenarios.
- Payroll:
/api/payroll/rate-book, /api/payroll/productions?production_id=.
- Discovery:
/api/manifest (entity ids, record counts), /health.
- The compensation and payroll rate books contain a
business_rules array — read it; the formulas below are derived from it.
Data shapes
finance/records: list of rows, one per (branch, account). Each row has branch_id, region_id, account, and values = a dict mapping period label ("M1".."M24") to a number. There is no period field; index by values[period].
period-map: maps each period (M1..M24) to a fiscal_year. M1–M12 = FY2024, M13–M24 = FY2025. (FY#### label = "FY"+fiscal_year.)
accounts: each has account, category (revenue/cogs/sga/allocations/operating), metric_type (currency/count).
compensation/rosters: list of employees with weeks_by_quarter (Q1..Q4 weeks), years_of_service, title (or null), overscale_weekly, combined_overscale_includes_title (bool), notes.
compensation/scenarios: dict keyed by scenario id; each has year_plus_1 and year_plus_2 blocks with mws_growth, overscale_growth, seniority_growth, title_pct_multiplier.
payroll/productions: list with one production object containing schedule (services) and roster (musicians with flags + assigned_service_ids).
2. Output conventions (from every template description)
- Currency → round to 2 decimals. Percent / ratio / growth → round to 4 decimals. (A ratio like 24.59% is stored as
0.2459; a per-unit currency like ARPU is a dollar value rounded to 2 dp.)
- Lists use ascending stable ids unless a
rank field says otherwise. branch_ids ascending; per_musician ordered by musician_id; conflict_flags sorted alphabetically.
- Return exactly the
required_top_level_keys, with the nested key names from field_types. Period labels are the raw "M##" strings; fiscal-year labels are "FY2024"/"FY2025".
- Include a category/field only "when applicable" if the template says so (e.g. omit
substitute_adjustment when zero; per-musician categories maps only nonzero categories).
3. FINANCE domain (branch close + regional view) — VALIDATED
Income-statement line definitions (sum the listed accounts over the chosen period set)
revenue = product_revenue + service_revenue
cogs = direct_materials_cogs + direct_labor_cogs
gross_margin = revenue − cogs
sga = sales_sga + admin_sga + occupancy_sga
allocations = shared_service_allocations
ebitda = gross_margin − sga − allocations
A "period set" is either a single month (e.g. ["M24"]) or all 12 periods of a fiscal year (FY2024 = M1..M12, FY2025 = M13..M24). Sum each account's values across the period set, then combine.
Derived metrics (FY-level)
ebitda_margin = FY ebitda / FY revenue (4 dp).
arpu = FY revenue / SUM of monthly active_customers over the FY. Use the summed count, NOT the average (validated: average is wrong).
sales_per_labor_headcount = FY revenue / SUM of monthly labor_headcount over the FY. Sum, not average (validated in both the branch and regional tasks).
revenue_growth_pct = (FY2025 revenue − FY2024 revenue) / FY2024 revenue (4 dp). ebitda_growth_pct analogous.
- Month-over-month (
mom_revenue_variance): amount = current-month revenue − prior-month revenue; pct = amount / prior-month revenue (4 dp). The current/prior months come from the memo (close_period/prior_period).
Regional rollups & rankings
- A region's branch set = all branches whose
region_id matches (ascending ids). Get region_id from /api/finance/branches.
- Region FY aggregates (revenue/sga/allocations/ebitda) = sum of per-branch income statements.
- Region
ebitda_margin / sales_per_labor_headcount use region-summed revenue, ebitda, and summed labor headcount.
top_ebitda_branch_id / bottom_ebitda_branch_id = branch with max / min FY2025 ebitda in the region.
region_reconciliation_variance = region total ebitda − sum(branch ebitda) = 0.00 (it ties out; this field tests that you aggregated consistently).
ebitda_rank_desc (branch within region) = 1 for highest FY ebitda, descending.
Cross-branch rankings (all 12 branches)
- "Sales growth" rank = by revenue-growth PERCENT (FY2025 vs FY2024), descending. NOT absolute dollar growth (validated: absolute-amount ranking is wrong). Rank 1 = highest %.
top_sales_growth_branch_id = branch with highest FY revenue-growth %.
top_arpu_branch_id = branch with highest ARPU (ranking is identical under sum or average since the /12 is a constant).
Finance SOP
- Read memo: target branch/region id, close + prior month, comparison years, focus list.
- GET
branches, period-map, accounts, and records (filter by branch_id or region).
- Build period sets (single month, FY2024, FY2025). Compute income statement per the line defs.
- Compute derived metrics (sum-based ARPU & sales-per-labor; margins/growth at 4 dp).
- Compute region context (sum branches, rank) and cross-branch rankings (growth % and ARPU).
- Assemble exactly the template keys; round currency 2 dp, ratios 4 dp.
4. COMPENSATION domain (summary + forecast) — VALIDATED ENGINE
Rate book (/api/compensation/rate-book) gives minimum_weekly_scale (MWS, e.g. 2520), pay_types (ordered list), seniority_weekly bands, title_premium_pct by title, current_year, and business_rules.
Per-employee weekly pay components (each multiplied by weeks worked that quarter, from weeks_by_quarter)
- Minimum Weekly Scale = MWS × weeks.
- Titled Position Premium =
title_premium_pct[title] × MWS × weeks. Suppress entirely (0) when combined_overscale_includes_title is true — the title premium is already folded into overscale per side letter (rate-book rule).
- Seniority =
seniority_weekly band amount for the employee's years_of_service × weeks. Bands (min–max years → weekly): 0–4 → 0; 5–9 → 48; 10–14 → 82; 15–19 → 126; 20–24 → 170; 25+ → 215 (read the actual table; last band has max_years: null).
- Overscale =
overscale_weekly × weeks.
Aggregation
quarter_totals[Qn] = sum over all employees of all four pay components for that quarter (using that quarter's weeks).
annual_pay_type_totals[paytype] = sum across all quarters/employees for that pay type.
annual_total = sum of all pay-type totals (= sum of quarter totals).
largest_pay_type = pay type with the largest annual total (typically Minimum Weekly Scale).
pay_types (list) = the rate book's pay_types order. current_year = rate book current_year. roster_count = len(roster).
combined_overscale_employee_count = # employees with combined_overscale_includes_title == true.
partial_quarter_employee_count = # employees with any quarter's weeks != 13 (a full quarter is 13 weeks; partials are flagged in notes).
Forecast (Year+1, Year+2) — scenario-driven
Pick the scenario by scenario_id from /api/compensation/scenarios. Apply growth compounding across forecast years:
- Add +1 year of service for Year+1 and +2 years for Year+2 to each employee's
years_of_service before selecting the seniority band (rate-book rule). Band jumps from this progression are a major driver.
- MWS(Year+k) = MWS × Π(1 +
mws_growth) over years 1..k (compound).
- Seniority weekly(Year+k) = band(adjusted yos) × Π(1 +
seniority_growth) compound. (Both the band change AND the growth multiplier apply — dropping the multiplier is wrong.)
- Overscale weekly(Year+k) =
overscale_weekly × Π(1 + overscale_growth) compound.
- Title premium uses MWS(Year+k) × pct × Π(
title_pct_multiplier) compound (still suppressed when combined-overscale).
annual_totals: current / year_plus_1 / year_plus_2. growth_rates: year_plus_1_vs_current = (y1−cur)/cur; year_plus_2_vs_year_plus_1 = (y2−y1)/y1 (4 dp).
largest_growth_pay_type = the pay type with the largest PERCENT growth from current → Year+2 (NOT absolute dollars). Seniority often wins because of band progression. (Validated: absolute-dollar basis is wrong.)
year_plus_2_quarter_totals / year_plus_2_pay_type_totals computed for the Year+2 forecast.
combined_overscale_employee_count / partial_quarter_employee_count as in the summary.
Note: the forecast numbers above match the live data to within a couple of fields; the compounding-growth-on-bands method is the best-validated reading. Apply growth multipliers AND the +years-of-service band rule together, compounding each year.
Compensation SOP
- Read memo: ensemble id, scenario id (forecast only), focus list.
- GET rate-book,
rosters?ensemble_id=, and (forecast) scenarios.
- Compute current-year per-employee components → quarter/pay-type/annual totals.
- (Forecast) Re-run for Year+1/Year+2 with +yos band rule and compounding scenario growth.
- Count combined-overscale and partial-quarter employees.
- Assemble exact template keys; currency 2 dp, growth 4 dp.
5. PAYROLL domain (weekly production) — STRUCTURAL FIELDS VALIDATED; pay math best-effort
Rate book (/api/payroll/rate-book): service_rates, premium_pct, service_time_limits, conflict_thresholds, weekly_guarantee, and business_rules. Production has schedule (services with service_type, duration_hours, start_time, end_time) and roster (musicians with assigned_service_ids and boolean flags principal, lead, quartet, electronic, substitute, vacation_eligible, integer doubles).
Service counts & conflict flags — VALIDATED (compute these exactly)
service_counts = count of each service_type string across the schedule.
conflict_flags (sorted, deduped) using only these enum values:
REHEARSAL_EARLY_START — any Rehearsal start_time earlier than conflict_thresholds.rehearsal_earliest_start (09:00).
REHEARSAL_LATE_END — any Rehearsal end_time later than rehearsal_latest_end (18:30).
SERVICE_OVER_TIME_LIMIT — any service whose duration_hours exceeds service_time_limits[service_type].
SOUND_CHECK_DURATION_MISMATCH — a sound-check service whose duration_hours != its named hours (1hr Sound Check → 1.0, 2hr Sound Check → 2.0).
top_paid_musician_id = musician with the highest weekly total (relative ordering is robust even if absolute pay is uncertain).
Pay math — apply the rate-book business_rules literally (authoritative source)
Per musician, over their assigned_service_ids:
- Base service pay: Performance, Audit, and Sound Check are per-service flat rates (
service_rates[service_type]). Rehearsal is hourly (service_rates["Rehearsal"] × hours) with a three-hour minimum call (use max(duration_hours, 3)).
- Premiums (
premium_pct) are applied to base service pay before vacation: principal_or_lead 15% (if principal or lead), quartet 15%, electronic 25%, concertmaster 20%. Doubles: 25% for the first extra instrument + 10% for each additional (doubles count): first_double once + additional_double × (doubles−1). Report the doubles amount under the separate doubles category and all other premiums under premium.
- Vacation = 4% of (base service pay + premiums) when
vacation_eligible is true.
- Weekly guarantee adjustment =
weekly_guarantee (2082) − base service pay, applied only to guaranteed regular players (non-substitutes) whose base service pay is below the guarantee.
- A musician's
total = base + premiums + doubles + vacation + guarantee_adjustment (+ substitute_adjustment if any). per_musician.categories maps only nonzero categories.
category_totals keys: performance, audit, rehearsal, sound_check, premium, doubles, vacation, guarantee_adjustment, and substitute_adjustment when applicable. weekly_total = sum of all musician totals.
CAUTION: in training, the structural fields (service_counts, conflict_flags, top_paid_musician_id) verified correct, but the exact monetary figures did not fully reconcile — the precise base/premium/vacation/guarantee interaction has unresolved nuance (candidates to reconsider for a new task: whether premiums/doubles apply to all base vs performance-only base, whether over-time-limit rehearsals are capped at the limit, and whether the guarantee compares base vs base+premium). Default to the literal rate-book wording above (premiums on full base service pay, rehearsal at actual hours with 3-hour minimum, guarantee on base service pay), since the rate book is the authoritative spec, and double-check arithmetic against the rate-book rules.
Payroll SOP
- Read memo: production id, focus list.
- GET payroll rate-book and
productions?production_id=.
- Compute
service_counts and conflict_flags (validated logic above) first.
- For each musician (ordered by
musician_id): sum base service pay by category, apply premiums/doubles, vacation, then guarantee; build nonzero categories; total.
- Roll up
category_totals and weekly_total; pick top_paid_musician_id.
- Assemble exact template keys; currency 2 dp; sort flags; order musicians by id.
6. General checklist for an unseen task in this group
- Identify the domain from
prompt.txt (finance / compensation / payroll) and the target id + focus from request_memo.json.
- Open
answer_template.json; list the exact required_top_level_keys and nested field_types — build your output to match key-for-key.
- Fetch only the relevant endpoints (remote base URL); ignore distractor names in
memo_note.
- Apply the validated domain formulas above (period→FY mapping, income-statement lines, sum-based ARPU/sales-per-labor, percent-based growth rankings, compensation pay components with combined-overscale/partial-quarter rules, compounding forecast growth with +years-of-service, payroll per-service/premium/vacation/guarantee and conflict flags).
- Round: currency 2 dp, percent/ratio/growth 4 dp. Order lists by ascending stable id unless a rank says otherwise; sort conflict flags alphabetically; include "when applicable" fields only when nonzero.
- Emit a single JSON object with exactly the template's top-level keys.
1---2name: finance-ops-reporting-63description: SOP for Crescent Finance Ops branch reporting, CBA compensation forecasting, and theatre payroll control tasks against the remote Finance Ops API.4---56# Crescent Finance Ops Reporting SOP78This skill covers three task families served by one read-only HTTP API:91. **Finance** — branch monthly-close packages and regional management views (income statement, growth, ratios, rankings).102. **Compensation** — ensemble (orchestra) current-year summaries and multi-year board forecasts from rate books, rosters, and scenarios.113. **Payroll** — weekly touring-production payroll control (per-service pay, premiums, vacation, guarantees, CBA conflict flags).1213Each task ships three payload files. Read all three before computing:14- `prompt.txt` — domain + what to produce.15- `payloads/request_memo.json` — the target entity id (branch/region/ensemble/production), periods/years, scenario id, and a `*_focus` list of what to report. **`memo_note` text is flavor/distractor — ignore unrelated names (e.g. "Harbor North draft workbook"); always reconcile against the live API data for the requested target id.**16- `payloads/answer_template.json` — the **authoritative contract**: `required_top_level_keys` (return exactly these keys), `field_types` (exact nested key names + shapes), and a `description` line with rounding/ordering rules. Mirror its key names and nesting verbatim.1718## 1. Remote API access1920- Base URL: `<remote-env-url>` (ignore any `base_url` inside payloads such as `127.0.0.1:*` — they are wrong).21- All reads are HTTP GET. Fetch with `curl -s "<base>/<endpoint>"`.22- Endpoints by domain:23 - Finance: `/api/finance/branches`, `/api/finance/period-map`, `/api/finance/accounts`, `/api/finance/records` (filters `?branch_id=` `?region=` `?account=`).24 - Compensation: `/api/compensation/rate-book`, `/api/compensation/rosters?ensemble_id=`, `/api/compensation/scenarios`.25 - Payroll: `/api/payroll/rate-book`, `/api/payroll/productions?production_id=`.26 - Discovery: `/api/manifest` (entity ids, record counts), `/health`.27- The compensation and payroll rate books contain a `business_rules` array — **read it; the formulas below are derived from it.**2829### Data shapes30- `finance/records`: list of rows, one per (branch, account). Each row has `branch_id`, `region_id`, `account`, and `values` = a dict mapping period label (`"M1"`..`"M24"`) to a number. There is **no** `period` field; index by `values[period]`.31- `period-map`: maps each `period` (`M1`..`M24`) to a `fiscal_year`. **M1–M12 = FY2024, M13–M24 = FY2025.** (FY#### label = `"FY"+fiscal_year`.)32- `accounts`: each has `account`, `category` (revenue/cogs/sga/allocations/operating), `metric_type` (currency/count).33- `compensation/rosters`: list of employees with `weeks_by_quarter` (Q1..Q4 weeks), `years_of_service`, `title` (or null), `overscale_weekly`, `combined_overscale_includes_title` (bool), `notes`.34- `compensation/scenarios`: dict keyed by scenario id; each has `year_plus_1` and `year_plus_2` blocks with `mws_growth`, `overscale_growth`, `seniority_growth`, `title_pct_multiplier`.35- `payroll/productions`: list with one production object containing `schedule` (services) and `roster` (musicians with flags + `assigned_service_ids`).3637## 2. Output conventions (from every template `description`)38- **Currency → round to 2 decimals.** **Percent / ratio / growth → round to 4 decimals.** (A ratio like 24.59% is stored as `0.2459`; a per-unit currency like ARPU is a dollar value rounded to 2 dp.)39- Lists use **ascending stable ids** unless a `rank` field says otherwise. `branch_ids` ascending; `per_musician` ordered by `musician_id`; `conflict_flags` sorted alphabetically.40- Return **exactly** the `required_top_level_keys`, with the nested key names from `field_types`. Period labels are the raw `"M##"` strings; fiscal-year labels are `"FY2024"`/`"FY2025"`.41- Include a category/field only "when applicable" if the template says so (e.g. omit `substitute_adjustment` when zero; per-musician `categories` maps only **nonzero** categories).4243## 3. FINANCE domain (branch close + regional view) — VALIDATED4445### Income-statement line definitions (sum the listed accounts over the chosen period set)46- `revenue` = `product_revenue` + `service_revenue`47- `cogs` = `direct_materials_cogs` + `direct_labor_cogs`48- `gross_margin` = `revenue` − `cogs`49- `sga` = `sales_sga` + `admin_sga` + `occupancy_sga`50- `allocations` = `shared_service_allocations`51- `ebitda` = `gross_margin` − `sga` − `allocations`5253A "period set" is either a single month (e.g. `["M24"]`) or all 12 periods of a fiscal year (FY2024 = M1..M12, FY2025 = M13..M24). Sum each account's `values` across the period set, then combine.5455### Derived metrics (FY-level)56- `ebitda_margin` = FY `ebitda` / FY `revenue` (4 dp).57- `arpu` = FY `revenue` / **SUM** of monthly `active_customers` over the FY. **Use the summed count, NOT the average** (validated: average is wrong).58- `sales_per_labor_headcount` = FY `revenue` / **SUM** of monthly `labor_headcount` over the FY. **Sum, not average** (validated in both the branch and regional tasks).59- `revenue_growth_pct` = (FY2025 revenue − FY2024 revenue) / FY2024 revenue (4 dp). `ebitda_growth_pct` analogous.60- Month-over-month (`mom_revenue_variance`): `amount` = current-month revenue − prior-month revenue; `pct` = amount / prior-month revenue (4 dp). The current/prior months come from the memo (`close_period`/`prior_period`).6162### Regional rollups & rankings63- A region's branch set = all branches whose `region_id` matches (ascending ids). Get region_id from `/api/finance/branches`.64- Region FY aggregates (revenue/sga/allocations/ebitda) = **sum of per-branch income statements**.65- Region `ebitda_margin` / `sales_per_labor_headcount` use region-summed revenue, ebitda, and summed labor headcount.66- `top_ebitda_branch_id` / `bottom_ebitda_branch_id` = branch with max / min FY2025 ebitda in the region.67- `region_reconciliation_variance` = region total ebitda − sum(branch ebitda) = **0.00** (it ties out; this field tests that you aggregated consistently).68- `ebitda_rank_desc` (branch within region) = 1 for highest FY ebitda, descending.6970### Cross-branch rankings (all 12 branches)71- **"Sales growth" rank = by revenue-growth PERCENT (FY2025 vs FY2024), descending. NOT absolute dollar growth** (validated: absolute-amount ranking is wrong). Rank 1 = highest %.72- `top_sales_growth_branch_id` = branch with highest FY revenue-growth %.73- `top_arpu_branch_id` = branch with highest ARPU (ranking is identical under sum or average since the /12 is a constant).7475### Finance SOP761. Read memo: target branch/region id, close + prior month, comparison years, focus list.772. GET `branches`, `period-map`, `accounts`, and `records` (filter by branch_id or region).783. Build period sets (single month, FY2024, FY2025). Compute income statement per the line defs.794. Compute derived metrics (sum-based ARPU & sales-per-labor; margins/growth at 4 dp).805. Compute region context (sum branches, rank) and cross-branch rankings (growth % and ARPU).816. Assemble exactly the template keys; round currency 2 dp, ratios 4 dp.8283## 4. COMPENSATION domain (summary + forecast) — VALIDATED ENGINE8485Rate book (`/api/compensation/rate-book`) gives `minimum_weekly_scale` (MWS, e.g. 2520), `pay_types` (ordered list), `seniority_weekly` bands, `title_premium_pct` by title, `current_year`, and `business_rules`.8687### Per-employee weekly pay components (each multiplied by weeks worked that quarter, from `weeks_by_quarter`)88- **Minimum Weekly Scale** = MWS × weeks.89- **Titled Position Premium** = `title_premium_pct[title]` × MWS × weeks. **Suppress entirely (0) when `combined_overscale_includes_title` is true** — the title premium is already folded into overscale per side letter (rate-book rule).90- **Seniority** = `seniority_weekly` band amount for the employee's `years_of_service` × weeks. Bands (min–max years → weekly): 0–4 → 0; 5–9 → 48; 10–14 → 82; 15–19 → 126; 20–24 → 170; 25+ → 215 (read the actual table; last band has `max_years: null`).91- **Overscale** = `overscale_weekly` × weeks.9293### Aggregation94- `quarter_totals[Qn]` = sum over all employees of all four pay components for that quarter (using that quarter's weeks).95- `annual_pay_type_totals[paytype]` = sum across all quarters/employees for that pay type.96- `annual_total` = sum of all pay-type totals (= sum of quarter totals).97- `largest_pay_type` = pay type with the largest annual total (typically Minimum Weekly Scale).98- `pay_types` (list) = the rate book's `pay_types` order. `current_year` = rate book `current_year`. `roster_count` = len(roster).99- `combined_overscale_employee_count` = # employees with `combined_overscale_includes_title` == true.100- `partial_quarter_employee_count` = # employees with **any** quarter's weeks != 13 (a full quarter is 13 weeks; partials are flagged in `notes`).101102### Forecast (Year+1, Year+2) — scenario-driven103Pick the scenario by `scenario_id` from `/api/compensation/scenarios`. Apply growth **compounding** across forecast years:104- Add **+1 year** of service for Year+1 and **+2 years** for Year+2 to each employee's `years_of_service` **before** selecting the seniority band (rate-book rule). Band jumps from this progression are a major driver.105- MWS(Year+k) = MWS × Π(1 + `mws_growth`) over years 1..k (compound).106- Seniority weekly(Year+k) = band(adjusted yos) × Π(1 + `seniority_growth`) compound. (Both the band change AND the growth multiplier apply — dropping the multiplier is wrong.)107- Overscale weekly(Year+k) = `overscale_weekly` × Π(1 + `overscale_growth`) compound.108- Title premium uses MWS(Year+k) × pct × Π(`title_pct_multiplier`) compound (still suppressed when combined-overscale).109- `annual_totals`: current / year_plus_1 / year_plus_2. `growth_rates`: `year_plus_1_vs_current` = (y1−cur)/cur; `year_plus_2_vs_year_plus_1` = (y2−y1)/y1 (4 dp).110- `largest_growth_pay_type` = the pay type with the largest **PERCENT** growth from current → Year+2 (NOT absolute dollars). Seniority often wins because of band progression. (Validated: absolute-dollar basis is wrong.)111- `year_plus_2_quarter_totals` / `year_plus_2_pay_type_totals` computed for the Year+2 forecast.112- `combined_overscale_employee_count` / `partial_quarter_employee_count` as in the summary.113114> Note: the forecast numbers above match the live data to within a couple of fields; the compounding-growth-on-bands method is the best-validated reading. Apply growth multipliers AND the +years-of-service band rule together, compounding each year.115116### Compensation SOP1171. Read memo: ensemble id, scenario id (forecast only), focus list.1182. GET rate-book, `rosters?ensemble_id=`, and (forecast) `scenarios`.1193. Compute current-year per-employee components → quarter/pay-type/annual totals.1204. (Forecast) Re-run for Year+1/Year+2 with +yos band rule and compounding scenario growth.1215. Count combined-overscale and partial-quarter employees.1226. Assemble exact template keys; currency 2 dp, growth 4 dp.123124## 5. PAYROLL domain (weekly production) — STRUCTURAL FIELDS VALIDATED; pay math best-effort125126Rate book (`/api/payroll/rate-book`): `service_rates`, `premium_pct`, `service_time_limits`, `conflict_thresholds`, `weekly_guarantee`, and `business_rules`. Production has `schedule` (services with `service_type`, `duration_hours`, `start_time`, `end_time`) and `roster` (musicians with `assigned_service_ids` and boolean flags `principal`, `lead`, `quartet`, `electronic`, `substitute`, `vacation_eligible`, integer `doubles`).127128### Service counts & conflict flags — VALIDATED (compute these exactly)129- `service_counts` = count of each `service_type` string across the `schedule`.130- `conflict_flags` (sorted, deduped) using only these enum values:131 - `REHEARSAL_EARLY_START` — any Rehearsal `start_time` earlier than `conflict_thresholds.rehearsal_earliest_start` (09:00).132 - `REHEARSAL_LATE_END` — any Rehearsal `end_time` later than `rehearsal_latest_end` (18:30).133 - `SERVICE_OVER_TIME_LIMIT` — any service whose `duration_hours` exceeds `service_time_limits[service_type]`.134 - `SOUND_CHECK_DURATION_MISMATCH` — a sound-check service whose `duration_hours` != its named hours (`1hr Sound Check` → 1.0, `2hr Sound Check` → 2.0).135- `top_paid_musician_id` = musician with the highest weekly total (relative ordering is robust even if absolute pay is uncertain).136137### Pay math — apply the rate-book `business_rules` literally (authoritative source)138Per musician, over their `assigned_service_ids`:139- **Base service pay**: Performance, Audit, and Sound Check are **per-service** flat rates (`service_rates[service_type]`). Rehearsal is **hourly** (`service_rates["Rehearsal"]` × hours) with a **three-hour minimum call** (use `max(duration_hours, 3)`).140- **Premiums** (`premium_pct`) are applied to base service pay **before vacation**: `principal_or_lead` 15% (if `principal` or `lead`), `quartet` 15%, `electronic` 25%, `concertmaster` 20%. **Doubles**: 25% for the first extra instrument + 10% for each additional (`doubles` count): `first_double` once + `additional_double` × (doubles−1). Report the doubles amount under the separate `doubles` category and all other premiums under `premium`.141- **Vacation** = 4% of (base service pay + premiums) when `vacation_eligible` is true.142- **Weekly guarantee adjustment** = `weekly_guarantee` (2082) − base service pay, applied **only to guaranteed regular players (non-substitutes) whose base service pay is below the guarantee**.143- A musician's `total` = base + premiums + doubles + vacation + guarantee_adjustment (+ substitute_adjustment if any). `per_musician.categories` maps only nonzero categories.144- `category_totals` keys: performance, audit, rehearsal, sound_check, premium, doubles, vacation, guarantee_adjustment, and substitute_adjustment **when applicable**. `weekly_total` = sum of all musician totals.145146> CAUTION: in training, the structural fields (service_counts, conflict_flags, top_paid_musician_id) verified correct, but the exact monetary figures did not fully reconcile — the precise base/premium/vacation/guarantee interaction has unresolved nuance (candidates to reconsider for a new task: whether premiums/doubles apply to all base vs performance-only base, whether over-time-limit rehearsals are capped at the limit, and whether the guarantee compares base vs base+premium). Default to the **literal rate-book wording above** (premiums on full base service pay, rehearsal at actual hours with 3-hour minimum, guarantee on base service pay), since the rate book is the authoritative spec, and double-check arithmetic against the rate-book rules.147148### Payroll SOP1491. Read memo: production id, focus list.1502. GET payroll rate-book and `productions?production_id=`.1513. Compute `service_counts` and `conflict_flags` (validated logic above) first.1524. For each musician (ordered by `musician_id`): sum base service pay by category, apply premiums/doubles, vacation, then guarantee; build nonzero `categories`; total.1535. Roll up `category_totals` and `weekly_total`; pick `top_paid_musician_id`.1546. Assemble exact template keys; currency 2 dp; sort flags; order musicians by id.155156## 6. General checklist for an unseen task in this group1571. Identify the domain from `prompt.txt` (finance / compensation / payroll) and the target id + focus from `request_memo.json`.1582. Open `answer_template.json`; list the exact `required_top_level_keys` and nested `field_types` — build your output to match key-for-key.1593. Fetch only the relevant endpoints (remote base URL); ignore distractor names in `memo_note`.1604. Apply the validated domain formulas above (period→FY mapping, income-statement lines, sum-based ARPU/sales-per-labor, percent-based growth rankings, compensation pay components with combined-overscale/partial-quarter rules, compounding forecast growth with +years-of-service, payroll per-service/premium/vacation/guarantee and conflict flags).1615. Round: currency 2 dp, percent/ratio/growth 4 dp. Order lists by ascending stable id unless a rank says otherwise; sort conflict flags alphabetically; include "when applicable" fields only when nonzero.1626. Emit a single JSON object with exactly the template's top-level keys.