AnyComp — the Compensation Decision OS
AnyComp turns a stated pay strategy and a budget into several scored, defensible ways to spend it — never one take-it-or-leave-it recommendation — so a compensation leader walks into the room with a choice, not a verdict.
A People Analytics Toolbox Product — one of the flagship surfaces a buyer actually meets. This is the buyer-facing decision experience; the band-math service and IRT-grade engine that sit underneath have their own component explainer at docs/explainers/anycomp.md. Built to the portfolio Explainer Standard v1.0. Every claim below is grounded in the real code and contracts (src/spokes/anycomp/ contract 1.8.0, status live in src/lib/contracts/registry.ts; surface src/surfaces/anycomp-decision/ + routes under src/app/(surfaces)/anycomp/). Anything not yet built is marked (TBD).
Manifesto — why it exists
Most compensation planning ends in a single number per person, produced in a one-off spreadsheet, with the strategy that drove it and the trade-offs it accepted both living in one analyst's head. When the executive team asks "why this and not something else?", the honest answer is usually a shrug and a re-run.
AnyComp removes that pain by changing the unit of the decision. The thing it hands a comp leader is not the answer — it is a small set of genuinely different answers to the same question, each spending the same budget, each scored on the same measures, each carrying its own labelled trade-offs. The organizing premise is written straight into the contract and the trade-off-radar component's own comment: "several scenarios, never one option." A leader doesn't want the tool to pick; they want a defensible menu and the authority to choose.
The differentiation beat, stated as the shift it forces:
- FROM a spreadsheet that yields one recommendation, with the strategy implicit and the trade-offs invisible.
- TO a service where the strategy is captured explicitly, the priorities are elicited with a real instrument, and the engine returns several scored scenarios with each one's spend and trade-offs made first-class.
How it differs from the obvious substitutes:
- vs. doing it by hand in a comp spreadsheet — 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. The band math, floor-binding, and optimizer are tested code, not fragile cell formulas; the same cycle run twice gives the same result.
- vs. generic comp-planning tools — those still resolve to a single plan. AnyComp refuses to collapse the decision: the pay fairness corner and the competitiveness corner arrive side by side, scored identically, so the choice between them is the executive's and is defensible line by line.
- vs. generic BI — a dashboard shows you where you are; it never proposes what to do about it within a budget. AnyComp's loop ends in selected actions with a cost waterfall, not a chart.
Visual — Tier B (FROM→TO typographic block).
one spreadsheet recommendation (strategy implicit · trade-offs hidden) → several scored scenarios (strategy explicit · priorities elicited · each trade-off labelled on the same axes)
Walkthrough — how it actually works
A comp leader meets AnyComp at one of two real surfaces under src/app/(surfaces)/anycomp/: the quick what-if (/anycomp/decision, rendered by DecisionWorkbench) and the full engagement (/anycomp/engagement, rendered by EngagementBuilder). Both end in the same ScenarioResults view. The journey is inputs → the decision loop → scored scenarios.
1 — State the priorities (100 pennies). The workbench presents five direct-outcome dimensions and asks the leader to spread 100 pennies across them — the same forced-allocation logic MaxDiff uses to make a preference cost something. The dimensions, with their workbench defaults, are: Competitiveness (40), Equity & consistency (30), Cost reduction (20), Performance differentiation (10), Optimization / waste (0). The allocation need not sum to 100 — the engine normalizes on submit.
2 — Set the budget. A single dollar figure (default $1,000,000 in the workbench). The full engagement additionally captures company context — business phase, headcount band, funding stage, industry, target market percentile — and a present↔future culture-marker grid where the gap is the intended change (e.g. confidential→transparent pay: now 30, target 60). These are stored on the CompensationStrategy; the allowed-action-category checkboxes gate which levers the optimizer may pull.
3 — The decision loop runs. Five real steps in core/decision/:
- Objective (
objective.ts) — maps the normalized priority weights onto per-measure weights through each measure's declaredpriorityContributions. This is the priorities → objective bridge. - Optimizer (
optimizer.ts) — a greedy budget allocation over the pay-action library. Each action is scored asΣ priority-weight × outcome-impact, ranked by bang-for-buck (score ÷ positive cost; zero/negative-cost aligned actions rank first as "free wins"), and admitted within budget. Misaligned actions (score ≤ 0) and disallowed categories are excluded. - Simulator (
simulator.ts) — applies the selected actions to the baseline and computes the measured deltas. The cost side is real net spend; the deep effect sizes are a deterministic, directionally-correct stub (a fixed fraction-of-gap move per action,STUB_EFFECT_PER_ACTION = 0.2) at the named Lane 1B / 2C seams. - Scenario generator (
scenario-generator.ts) — re-weights the objective to archetype corners plus the stated strategy, re-optimizes and re-simulates each, scores them all on every measure, and builds the trade-off radar. The four corners are your stated strategy, competitiveness-first ({competitiveness:80, performance_differentiation:20}), equity-first ({equity_consistency:80, competitiveness:20}), and cost-disciplined ({cost_reduction:70, optimization_waste_reduction:30}).
4 — Read the scenarios. ScenarioResults renders, for each scenario: a card with cost, headroom, action count and a per-category spend bar; a cost waterfall (where the spend lands); a measure-delta matrix (baseline → after, green improves / red worsens); and over the top, the trade-off radar — each scenario a distinct polygon across the five outcome axes. Distinct polygons = distinct bets.
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?" Each scenario is a labelled corner of the same budget; each action carries its observable mechanism and a falsification condition (the rigor discipline from Mike's decision-model paper), so the radar plus the per-action fields answer that question directly.
Visual — Tier B (the decision loop, as it runs in core/decision/).
strategy + 100-pennies priorities + budget → objective (priorities × measure-coupling) → optimizer (greedy bang-for-buck within budget) → simulator (actions → measured Δ; cost real, effect sizes stubbed) → scenarios (4 corners + trade-off radar)
What it enables
Concrete uses a compensation practitioner would recognize, each grounded in a real route or surface:
- Generate a scenario set in seconds — the
/anycomp/decisionwhat-if posts priorities + budget toPOST /api/surfaces/anycomp/scenariosand returns several distinct, scored scenarios with their trade-offs labelled, without persisting anything. The fastest path from "here's how we want to spend" to "here are four ways it plays out." - Run a full, persisted engagement —
/anycomp/engagementcaptures strategy + culture markers + priorities + budget, saves the strategy, priorities, scenarios, impacts and an audit row in one transaction via the engagement endpoint, and is reloadable any time. - Defend a corner to the exec team — the trade-off radar plus each action's mechanism/falsification fields turn "trust me" into "here's the lever, here's why it moves the measure, here's when it wouldn't."
- Constrain the levers to what's allowed — the allowed-action-category gates mean a board that has ruled out, say, broad budget increases simply unchecks
CostOptimization, and the optimizer never proposes it. - Compare a stated strategy against the disciplined extremes — every run includes the cost-disciplined and equity-first corners alongside the leader's own weighting, so the stated strategy is always read against its alternatives, not in isolation.
- Hand the band math + compliance floor downstream — the decision layer sits on top of the AnyComp engine (merit / variable-pay / equity / discretionary calculators) and wage-compliance floor-binding; once a scenario is chosen, those produce the per-employee numbers (see the component explainer).
Visual — Tier B (the five outcome axes the radar scores). Competitiveness · Equity & consistency · Cost reduction · Performance differentiation · Optimization / waste — each scenario normalized 0..1 against the strongest scenario on that axis, so the leader sees, in one figure, what each way of spending the same budget buys.
One load-bearing worked example
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 — at a $1,000,000 budget with the workbench-default priorities {competitiveness:40, equity_consistency:30, cost_reduction:20, performance_differentiation:10}.
The baseline is the exact deterministic output of computeBaselineMeasures() over that fixture (recomputed from 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 ~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 (e3 at $172,000 reports to e5 at $168,000).cost.total_comp_cost= $960,000 — 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.)
The four scenarios the generator returns are the deterministic output of the optimizer over the seed action library at this budget (computed from optimizer.ts + action-catalog.ts):
- Your stated strategy — selects
ACT_SHIFT_PAY_MIX_BY_SEGMENT,ACT_FREEZE_LOW_IMPACT_SEGMENTS,ACT_BUDGET_REALLOCATION_BY_IMPORTANCE,ACT_PAY_TRANSPARENCY_POLICY_SHIFT,ACT_COMPRESSION_CORRECTION,ACT_PAY_EQUITY_REMEDIATION_TARGETED,ACT_MARKET_FLOOR_ADJUSTMENT. Net spend $350,000, headroom $650,000 (the free/savings actions and cheap equity fixes dominate a balanced weighting). - Competitiveness-first — selects
ACT_SHIFT_PAY_MIX_BY_SEGMENT,ACT_BUDGET_REALLOCATION_BY_IMPORTANCE,ACT_INCREASE_MERIT_DIFFERENTIATION,ACT_ADJ_MARKET_TARGET_SEGMENT,ACT_MARKET_FLOOR_ADJUSTMENT. Net spend $1,000,000, headroom $0 — it spends the whole budget pulling the 75%-below-market figure down via the market-target lever. - Equity-first — selects
ACT_SHIFT_PAY_MIX_BY_SEGMENT,ACT_COMPRESSION_CORRECTION,ACT_PAY_TRANSPARENCY_POLICY_SHIFT,ACT_PAY_EQUITY_REMEDIATION_TARGETED,ACT_MARKET_FLOOR_ADJUSTMENT. Net spend $650,000, headroom $350,000 — it attacks the 0.0758 unexplained variance and the 0.1667 compression index with targeted (bidirectional) remediation rather than across-the-board cash. - Cost-disciplined — selects
ACT_FREEZE_LOW_IMPACT_SEGMENTS,ACT_BUDGET_REALLOCATION_BY_IMPORTANCE. Net spend −$300,000 (a saving), headroom $1,300,000 — it freezes low-impact segments and reallocates rather than spending.
What a practitioner does with it. The same $1,000,000 question yields four honestly different answers: spend it all on competitiveness, spend two-thirds on fairness with a third held back, save $300k, or take the balanced middle. The trade-off radar normalizes each corner's strength on every axis so the leader sees the shape of each bet at a glance — and chooses, defensibly, among several rather than rubber-stamping one. No figure here is invented; the baseline measures are the literal output of the spoke's seeded core, and the selections + spends are the deterministic output of its optimizer over the seed action library.
Visual — Tier B (the worked-example deltas). Equity-first drives internal.unexplained_variance and internal.compression_index down (improving) while leaving external.pct_below_market_target largely untouched; competitiveness-first does the reverse and spends the full budget to do it. The radar makes the mirror-image trade explicit. (A live Tier-A capture of POST /api/surfaces/anycomp/scenarios is available when a dev server is running; no server was up at authoring time, so the values above are computed deterministically from the same core the route calls.)
How it fits in the toolbox
AnyComp the Product is a composition — it talks to its sibling spokes over HTTP + vendored contracts only, never cross-spoke core/ imports. The boundary discipline is strict and deliberate.
- Sits on top of — the AnyComp spoke's own band-math + cycle engine + four CompEngine calculators (the component explainer is the source of truth for that layer). The decision layer is the buyer-facing face; the engine is the algorithm.
- Composes (as estimator seams) — the measures name their real estimator references: market position and the pay-equation residual point at
wage-benchmark; felt fairness points atpreference-modeler's fairness survey (the same MaxDiff machinery the 100-pennies elicitation echoes); job matching and importance point atjob-family-agent. In MVP-0 these seams are stubbed at the named Lane 1B / 2C points; the catalog shape and priority coupling are real. - Composes (deliberately omitted from this spoke) — the value-of-information question ("should we gather more evidence before approving this change?") is not in AnyComp; a consumer calls
forecasting's VOI endpoint for that. Likewise cohorts are resolved bysegmentation-studiobefore the call (caller-supplied segment IDs), and the legal floor comes fromwage-compliancefloor-binding. - Emits — the AnyComp contract (
ScenarioBundle,Scenario,ScenarioImpact,TradeoffRadar,Measure,Action,CompensationStrategy,PriorityWeights, and the rest). Consumers vendor a copy ofsrc/spokes/anycomp/contracts/types.ts. - Consumers meet it — at the exec surface
/anycomp/decision(100-pennies + trade-off radar) and/anycomp/engagement, and via the MCP toolanycomp.scenarios.generate. Performix is the first downstream consumer pattern.
Visual — Tier B (typographic data-flow). { wage-benchmark · preference-modeler · job-family-agent · wage-compliance floor · segmentation-studio cohorts } → AnyComp decision loop → { /anycomp/decision + /anycomp/engagement exec surfaces · MCP anycomp.scenarios.generate · downstream consumers }.
Commercialization / packaging
AnyComp is both a service component and the buyer-facing Product — the part a customer actually meets, branded as the Compensation Decision OS, the "several scenarios, never one option" loop.
- Where it sits: the band-math and engine layers are service primitives other comp tooling composes; the decision layer is the meal, not the ingredient. It is one of the toolbox's PA Products (alongside Leadership Index and the Analytics-Plan Generator).
- Data-license posture: the BLS-derived labor priors used by the underlying engine come 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
sourcelineage precisely so pooled canonical benchmarks stay distinguishable from tenant-ingested, license-bound overlays. The decision layer itself ships against a seeded matched population in MVP-0, so no licensed data is exposed in the demo path. - Pricing tiers and packaged offerings are (TBD) — not earned yet, so not stated.
Visual — (TBD — product-tier placement diagram showing decision layer as the meal over the engine/primitive ingredients).
Current status
Grounded in the real code state (contract 1.8.0, status live, src/spokes/anycomp/ + src/surfaces/anycomp-decision/):
- Shipped: the decision loop end to end —
core/decision/(objective, optimizer, simulator, scenario-generator), the canonical measures catalog (nine three-value-rooted measures with estimator seams + priority coupling), the seed action library (ten parameterized pay actions with signed outcome impacts, mechanisms, and falsification conditions); the exec surfaces/anycomp/decision(DecisionWorkbench) and/anycomp/engagement(EngagementBuilder) with the trade-off radar, cost waterfall, scenario cards, and measure-delta matrix; the routesPOST /scenarios,GET /measures,GET /actions,POST /engagements+GET /engagements/[strategyId], plus the surface-facingPOST /api/surfaces/anycomp/{scenarios,engagements}; the MCP toolanycomp.scenarios.generate(PAT-AC1). Multi-tenant scoping in place. - In flight / planned: the deep estimators are stubbed at the named 1B / 2C seams — real regression-measured market position, residual equity, survey felt-fairness, and the return-on-spend outcome model (the simulator's effect sizes are a directional stub today; cost is real). Scenario generation uses fixed archetype corners; the war-ready follow-up in
scenario-generator.tsis to move toward divergence-driven scenarios derived from genuine multi-executive disagreement. A static Tier-A capture of the rendered radar/scenarios and a product-tier diagram are documentation follow-ups.
Visual — Tier B (in-repo reference). The seed lives in core/decision/population.ts (the 8-person fixture), measures-catalog.ts (nine measures), and action-catalog.ts (ten actions); the decision tables land in drizzle/0099_pat_ac1_anycomp_decision_layer.sql. The simulator honestly stamps each scenario's confidence at 0.4 with drivers ["MVP-0 stubbed effect models", "stubbed market matching", "fixture population"].
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, with cost real and effect sizes a labelled stub at named seams. The arc is to replace each stub with its real cross-spoke estimator — market position from wage-benchmark, residual fairness from the regression, felt fairness from a preference-modeler survey, return-on-spend from an outcome model — and to evolve scenario generation from fixed archetype corners toward divergence-driven scenarios that surface where the executives actually disagree, so the menu it produces reflects the real fault lines of the decision rather than four canonical opposites.