AnyComp — plain-language explainer

AnyComp is a compensation decision engine: it turns a stated pay strategy and a budget into several scored, defensible spending scenarios — never a single take-it-or-leave-it option — and keeps the models, evaluations, and decisions versioned in a database rather than a spreadsheet.

A People Analytics Toolbox component. Built to the portfolio Explainer Standard v1.0. Every claim below is grounded in the spoke's own code and contracts (src/spokes/anycomp/, contract 1.8.0, status live in src/lib/contracts/registry.ts); anything not yet built is marked (TBD).


1. What is it?

AnyComp is the toolbox's compensation decision OS — a tenant-scoped service that does two related jobs. First, the band-math layer: it stores versioned compensation models (pay bands defined across dimensions like job family, level, and geo zone), evaluates an employee population against them, and persists a cycle-level audit of every recommendation. Second, the decision layer (PAT-AC1): it takes a compensation strategy plus leadership priorities plus a budget and generates several distinct, fully-scored scenarios — each one a bundle of pay actions with its measured trade-offs spelled out.

The organizing premise, drawn straight from the contract's own comment, is "several scenarios, never one option." A comp leader doesn't want the tool to pick for them; they want a small set of genuinely different ways to spend the same dollars, each scored on the same measures, so the choice is theirs and defensible.

Visual — Tier B (step flow). The decision loop, as it actually runs in core/decision/:

strategy → priorities (100-pennies / weight vectors) → objective (priorities × measure-coupling) → optimizer (greedy budget allocation) → simulator (actions → measured Δ) → scenarios (+ trade-off radar)

2. What problem does it solve — and why is it different?

The pain it removes: most compensation planning happens in a one-off spreadsheet that produces a single recommended number per person, with the rationale, the budget trade-offs, and the strategy that drove it all living in the analyst's head.

The difference, stated as a shift:

  • FROM a spreadsheet that yields one answer, with the strategy implicit and the trade-offs invisible.
  • TO a versioned service where models and evaluations are persisted in Postgres, write endpoints are service-key gated, every cycle run leaves an audit trail, and the decision layer returns several scored scenarios with their trade-offs made explicit.

How it differs from the obvious substitutes:

  • vs. doing it by hand in a comp spreadsheet — AnyComp versions the model and the recommendations, so two customers running the same cycle on the same employee IDs never collide (multi-tenant scoping, PAT-39-FU-D), and the band math, floor-binding, and engine calculators are tested code rather than fragile cell formulas.
  • vs. a generic comp-planning tool — AnyComp roots every measure in an explicit three-value model (external / internal / personal, plus cost and optimization meta-families) and scores every scenario on every measure. It refuses to collapse the decision to a single recommendation; the trade-off radar is a first-class output.

Visual — Tier B (FROM→TO typographic block). The shift above is the visual; a rendered comparison block is a follow-up (FU-A).

3. How does it work?

The practitioner's questions, answered with the real method:

"What goes in?" Inputs depend on which layer you call. For the band-math layer: a CompModel (bands with p25/p50/p75/p90 per dimension slice) and an array of EmployeeCompContext rows (each with dimensionValues and currentPay). For the decision layer: a CompensationStrategy (business phase, market-position targets, constraints, governance philosophy), a PriorityWeights vector (how much the leadership team weights competitiveness vs. equity vs. cost vs. performance differentiation vs. waste-reduction — captured via a 100-pennies / MaxDiff instrument), and a budget in dollars.

"What does it compute?" The decision loop, in core/decision/, runs five real steps. objective.ts maps priority weights onto measure weights through each measure's declared priority-coupling. optimizer.ts does a greedy budget allocation over the pay-action library. simulator.ts applies the selected actions to the baseline and computes the measured deltas (the cost side is real; the deep effect sizes are a directional stub at the named 1B/2C seams). scenario-generator.ts re-weights the objective to archetype corners (competitiveness-first, equity-first, cost-disciplined) plus the stated strategy, re-optimizes each, and scores all of them on every measure, then builds the trade-off radar.

"What comes out?" A ScenarioBundle: the baseline measure snapshot, several ScenarioWithImpact records (each with its selected actions, a per-category cost waterfall, budget headroom, and the simulated deltaMetrics), and a TradeoffRadar normalizing each scenario's strength on every outcome axis.

Data sources and the science backing. The measures name their real estimator seams: market position and the pay-equation residual point at wage-benchmark; felt fairness points at preference-modeler's fairness survey; compression and the pay-for-performance slope are computed over the population itself. The BLS labor-metrics layer (PAT-18-FU-A) carries voluntary-attrition, hire, and job-opening priors keyed by SOC × geography × period, sourced from BLS JOLTS / OEWS / CES — used to flag market-tier classification and exit-risk priors. The pay-action library carries the rigor discipline from Mike's decision-model paper: every action declares its observable mechanism, the measures it targets, and a falsification condition under which it would fail.

Differentiation beat: the practitioner's real question isn't "what's the recommended raise" — it's "can I walk the executive team through why this scenario and not that one?" The trade-off radar plus the per-action mechanism/falsification fields answer that directly: each scenario is a labelled corner of the same budget, scored on the same axes, with each action's rationale and failure mode written down.

Visual — Tier B (in-repo reference). The canonical measure catalog lives in core/decision/measures-catalog.ts — nine three-value-rooted measures, each with its valueFamily, estimatorRef, and priorityContributions; the seed action library is core/decision/action-catalog.ts (ten parameterized pay actions with signed outcome impacts).

4. What does it enable?

Concrete uses a compensation practitioner would recognize:

  • Generate a scenario set for a comp cycle — feed strategy + priorities + budget to POST /scenarios and get back several distinct, scored ways to spend the budget, with the trade-offs labelled, in one stateless call.
  • Run a defensible merit / equity / variable-pay / discretionary cycle — the four CompEngine calculators (/engines/*/run) compute per-employee increases from a merit matrix, a variable-pay payout model, an equity-budget allocation, and a discretionary pool, with idempotent persisted snapshots.
  • Evaluate a population against pay bands with a compliance floorPOST /evaluate returns per-employee recommendations, and when a wage-compliance floor is supplied (PAT-81) it clamps base pay so the annualized rate never falls below the legal minimum, flagging exactly which recommendations were raised.
  • Audit calibration drift across a cycle — the workbook-analytics bundle (PAT-160) computes comp-ratio drift histograms, a dual nine-box of performance × pay fairness, vs-plan budget variance, and pool-% -of-payroll metrics.
  • Price market movement forward — ingest market reference history, fit a pooled lag surrogate, and get a Monte-Carlo percentile forecast of year-over-year midpoint movement for a job family × level.
  • Persist a multi-session engagementPOST /engagements saves the strategy, priorities, scenarios, impacts, and an audit row in one transaction; GET /engagements/[strategyId] reloads the durable record.

Visual — (TBD — a rendered trade-off radar over the four seeded scenarios). The exec surface (src/surfaces/anycomp-decision/) renders this live; a static capture is a follow-up.

5. How it fits in the toolbox

Data flow:

  • Consumeswage-compliance floors (the jurisdictions.resolve → rules → anycomp.evaluate chain, PAT-81); cohorts resolved by segmentation-studio before the call (caller-supplied segmentIds, no cross-spoke runtime imports); BLS public reference data for the labor-metrics layer; and, at the named decision-layer estimator seams, wage-benchmark (market position, regression residual), preference-modeler (felt-fairness survey), and job-family-agent (job matching, importance).
  • Composes (but does not import) — the market-movement forecast reuses calculus's surrogate-model machinery (PAT-147-G) via persisted tables, not cross-spoke core/ imports.
  • Emits — the AnyComp contract (CompModel, CompRecommendation, ScenarioBundle, Measure, Action, and the rest). Consumers vendor a copy of src/spokes/anycomp/contracts/types.ts.
  • Feeds — the AnyComp decision-layer Product (one of the toolbox's PA Products / "meals") and its exec surface at /anycomp/decision. Performix is the first downstream consumer pattern.

The boundary discipline is strict: AnyComp talks to its sibling spokes over HTTP + vendored contracts only. The decision-model VOI question ("should we gather more information before approving this change?") is deliberately out of this spoke — a consumer calls forecasting's VOI endpoint for that.

Visual — Tier B (typographic data-flow). { wage-compliance floor · segmentation-studio cohorts · wage-benchmark · preference-modeler · BLS priors } → AnyComp → { decision-layer Product · /anycomp/decision exec surface · downstream consumers }.

6. Commercialization / packaging

AnyComp is both a service component and the engine behind a buyer-facing Product. The band-math and engine layers are service primitives other comp tooling composes; the decision layer is the part a buyer actually meets — branded as the AnyComp Compensation Decision OS, the "several scenarios, never one option" loop.

  • Data-license posture: the BLS labor-metrics priors derive from public BLS data (JOLTS / OEWS / CES — U.S. government, freely usable). Vendor-survey market references (Radford, Mercer, Willis Towers Watson) are a separate, license-constrained concern — the market-reference history table tags each row with its source lineage precisely so pooled canonical benchmarks stay distinguishable from tenant-ingested, license-bound overlays.
  • Anything about pricing tiers or packaged offerings is (TBD) — not earned yet, so not stated.

Visual — (TBD — product-tier placement diagram).

7. The vision

A compensation decision you can defend line by line: every dollar traced to a stated priority, every scenario scored on the same three-value measures, every action carrying its own mechanism and falsification condition — and the engine honest about which effect sizes it has actually measured versus seeded.

The decision layer shipped as MVP-0: the loop is real and runs against a seeded matched population, but the deep estimators (regression-measured market position, residual equity, survey felt-fairness, the return-on-spend outcome model) are stubbed at named seams (Lanes 1B / 2C in docs/anycomp/BUILD-PLAN.md). The arc is to replace each stub with its real cross-spoke estimator, and to move scenario generation from fixed archetype corners toward divergence-driven scenarios derived from genuine multi-executive disagreement (the war-ready follow-up noted in scenario-generator.ts).

8. Current status

Grounded in the real code state (contract 1.8.0, status live, src/spokes/anycomp/):

  • Shipped: versioned comp models + bands + stateless /evaluate + persisted /cycles/run audit (PAT-18); wage-compliance floor-binding (PAT-81); BLS labor-metrics priors with labor-rate / exit-risk / tier-classify reads (PAT-18-FU-A); the four CompEngine calculators — merit, variable-pay, equity, discretionary (PAT-147-B); engagement-parity comp-data shapes (PAT-147-FU-A); market-movement history + fit + forecast (PAT-147-G); the workbook-analytics bundle — vs-plan, pool-metrics, HR-finance crossover, comp-lifecycle, CR-drift histogram, dual nine-box (PAT-160); and the decision layer/scenarios, /measures, /actions, /engagements, with the MCP tool anycomp.scenarios.generate (PAT-AC1). MCP tools registered; multi-tenant scoping in place (PAT-39-FU-D).
  • In flight / planned: the decision-layer deep estimators (currently stubbed at the 1B/2C seams) — real market-position, equity-residual, felt-fairness, and return-on-spend models; divergence-driven scenarios from multi-exec disagreement; FX conversion for cross-currency populations (PAT-96-FU-B); read-path tenant filtering (PAT-39-FU-D-FU).

Visual — Tier B (in-repo reference). The seed: migrations 0015_pat18_anycomp.sql (DDL) + 0016_pat18_anycomp_seed.sql install model b18e0000-0000-4000-a000-000000000001 (3 levels × 2 US regions) and five comp_evaluations rows for cycle pat-18-seed-cycle; the decision tables land in drizzle/0099_pat_ac1_anycomp_decision_layer.sql.


Worked example (real, computed from the spoke's own decision-layer core)

This runs the decision loop against the eight-person seeded matched population in core/decision/population.ts — two job families (ENG, SUP) across five levels, with a manager chain and a performance spread. The baseline measure values below are the exact deterministic output of computeBaselineMeasures() over that fixture (recomputed from the source, not invented):

  • external.pct_below_market_target = 0.75 — six of eight incumbents sit below their target market position.
  • external.avg_market_ratio = 0.9257 — the population averages about 93% of market reference.
  • internal.unexplained_variance = 0.0758 — the within-(family×level) pay-dispersion proxy.
  • internal.compression_index = 0.1667 — one of six report→manager pairs is compressed (a report paid at or above their manager: e3 at $172,000 reports to e5 at $168,000).
  • cost.total_comp_cost = $960,000 — the summed annual total cash across the eight.
  • optimization.budget_to_high_impact = 0.6542 — ~65% of spend sits on high-importance roles.

(personal.felt_fairness = 0.6 and optimization.return_on_spend = 0.5 are seeded STUBs in the fixture, flagged as such in the code — they await the Lane 2C survey and the outcome model.)

What a practitioner does with it. Feeding these into POST /scenarios with a budget and a priority vector returns four labelled scenarios — your stated strategy, competitiveness-first, equity-first, and cost-disciplined — each a different selection from the action library. A competitiveness-first corner leans on ACT_ADJ_MARKET_TARGET_SEGMENT and ACT_INCREASE_OVERALL_BUDGET to pull the 75% "below market" figure down (at a real dollar cost shown in the per-category waterfall); an equity-first corner leans on ACT_PAY_EQUITY_REMEDIATION_TARGETED and ACT_COMPRESSION_CORRECTION to attack the 0.0758 unexplained variance and the 0.1667 compression index instead. The trade-off radar normalizes each corner's strength on every axis so the comp leader sees, in one view, what each way of spending the same budget buys — and chooses, defensibly, among several rather than rubber-stamping one. No figure in this example is invented; the baseline measures are the literal output of the spoke's seeded core.