Analytics-Plan Generator — plain-language explainer

Analytics-Plan Generator turns "what should we even measure?" into a ranked, defensible plan: it reads what the leadership team actually cares about, finds the models that speak to those priorities, and tells you which measurements buy the most decision-confidence per dollar — measure these, in this order, and skip the rest.

A People Analytics Toolbox Product — one of the composed surfaces a buyer meets, not a single spoke. Its engine lives in the factor-models spoke (contract 0.3.0, src/spokes/factor-models/core/analytics-plan.ts); it composes value-of-information inputs computed upstream in forecasting. Every claim below is grounded in that code; anything not yet built is marked (TBD). For the underlying spoke's own mechanics, see the factor-models spoke explainer — this page is the Product framing and does not duplicate it.


Lead

Most analytics work starts by building a dashboard and hoping the right question was on it. Analytics-Plan Generator starts from the decision and works backwards to the smallest set of measurements that will actually move it.


Manifesto — why it exists

The default failure mode of a People Analytics function is measuring what is easy, then looking for a decision it might inform. You get a dashboard with forty tiles, a quarterly survey nobody acts on, and a standing request to "add a metric for X." None of it is anchored to a decision, so none of it has a defensible stopping rule — there is always one more cut to add.

Analytics-Plan Generator inverts that. It treats measurement as an investment under uncertainty: a measurement is worth running only to the extent that resolving its uncertainty changes a decision, net of what the measurement costs. That is the logic of value of information — the expected value of perfect information (EVPI) on a question, discounted by how much of that a realistic study actually captures, minus the cost to run it. Measures whose net value comes out negative are an explicit don't-measure signal, not a gap to fill.

The differentiation beat, stated as the shift:

  • FROM a dashboard built first, with the decisions it serves left implicit — and no honest way to say "stop measuring this."
  • TO a plan built from the leadership team's stated priorities, where every proposed measurement carries its expected net value, ranked highest-first, with the low-value ones flagged so you can defensibly not run them.

How it differs from the obvious substitutes:

  • vs. doing it by hand — the by-hand version is a workshop where everyone advocates for their favourite metric and the loudest voice wins. The Generator makes the weighting explicit (priorities are numbers that sum to a whole), the relevance deterministic (the same inputs always produce the same ranking), and the value-of-information arithmetic visible.
  • vs. generic BI / "build every chart" — BI tools optimize for display of whatever data exists. This optimizes for which questions are worth answering at all, before anything is built.
  • vs. a measurement vendor's standard battery — a vendor sells you their instrument regardless of whether your decisions need it. The Generator ranks candidate models against your priorities and can rank a candidate to the bottom.

Visual — Tier B (FROM→TO typographic block). The shift above is the manifesto's visual; a rendered comparison panel on a public surface is a follow-up (TBD — FROM→TO comparison component).


Walkthrough — how it actually works

The Generator is a stateless composite: it reads no database at compute time. Everything it needs arrives on the request, and the same request always yields the same plan. The practitioner's path through it has three movements.

Movement 1 — Inputs: priorities + candidate models. Two things go in:

  • priorityWeights — a map of priority/category tags to weights ({ "retention": 70, "innovation": 30 }). These are the leadership team's stated priorities. You can pass raw allocations (0–100, percentages, a 100-penny budget) because the engine normalizes them internally to sum to 1. In the composed Product, these weights are the natural output of an elicitation exercise run in preference-modeler (forced-choice / penny-allocation), but the engine itself just takes the numbers.
  • candidateModels — the models you could invest in. Each carries a name, an optional category (e.g. "culture", "leadership"), a list of priorityTags it speaks to, and a list of measures. Each measure carries its value-of-information inputs: evpi, an optional typicalReduction (the fraction of EVPI a realistic study captures — an EVSI/EVPI proxy, defaulting to 1), and an optional typicalCost (defaulting to 0). Those VOI numbers are computed upstream in forecasting and handed in — the Generator does not invent them.

Movement 2 — Rank the models by priority-relevance. For each candidate model, the engine sums the normalized priority weights over the model's priorityTags. A model tagged retention inherits retention's normalized weight as its relevance. On top of that, if the model's category matches a high-weight priority — defined as a normalized weight at or above the mean across present priorities — it earns a flat +0.2 category bonus (CATEGORY_HIGH_WEIGHT_BONUS). Models sort descending by relevance, with a stable alphabetical tie-break on name so equal-relevance output is deterministic.

Movement 3 — Build the VOI-ranked measurement plan. Across all candidate models, every measure is scored: expectedNetVoi = evpi × (typicalReduction ?? 1) − (typicalCost ?? 0). The list sorts descending by net VOI (tie-break on modelId then measureId). Each measure also gets a priority bucket:

  • high — net VOI exceeds its cost (or is simply positive when no cost was supplied).
  • medium — net VOI is positive but does not clear the -cost bar.
  • low — net VOI is ≤ 0: the don't-measure signal.

The output is a single AnalyticsPlan: rankedModels (which lenses are most relevant to your priorities) and measurementPlan (which specific measures to run, in value order, with their buckets).

Differentiation beat: the practitioner's real question isn't "what could we measure" — it's "what is the shortest list I can defend to the CHRO, and where do I stop?" The high/medium/low bucketing plus the negative-VOI don't-measure flag answers exactly that: the plan tells you both what to do first and what to leave on the table.

Developer's path through the primitive. The engine is exposed as one stateless POST route, factor-models.analytics-plan (POST /api/spokes/factor-models/analytics-plan), guarded by the standard service-key check on writes; request and response both validate against the zod contract (AnalyticsPlanRequestSchemaAnalyticsPlanSchema). A consumer vendors src/spokes/factor-models/contracts/types.ts and calls the route — no cross-spoke internal imports.

Visual — Tier B (step-flow block). priorities (preference-modeler) + candidate models (factor-models) + VOI inputs (forecasting)[rank models by priority-relevance][score every measure by net VOI]AnalyticsPlan { rankedModels · measurementPlan(high|medium|low) }. A live Tier-A curl capture is held until the route is exercised with the service key against a deployed instance (TBD — live curl+JSON capture).


What it enables

Concrete uses a People Analytics leader would recognize:

  • Turn an exec offsite into a measurement plan. Run a penny-allocation across leadership, feed the resulting priority weights in, and get back the ranked plan instead of a wishlist.
  • Defend a "we are not measuring that" decision. When a stakeholder demands a metric, show the net-VOI bucket: a low measure is a documented, arithmetic-backed reason to skip it.
  • Sequence a research roadmap. The measurementPlan is already ordered by expected net value, so the highest-leverage study is at the top of the list — not the one whose owner argued hardest.
  • Choose between competing models / instruments. When two vendor batteries or internal models compete, rankedModels scores each against the actual priorities rather than against a sales deck.
  • Re-plan when priorities shift. Because the engine is stateless and deterministic, re-running with updated priority weights instantly re-ranks both models and measures — a reorg or strategy change re-prices the whole plan.
  • Pressure-test a proposed dashboard. Map each proposed tile to a candidate measure with its VOI inputs; the buckets reveal which tiles earn their place.

Visual — (TBD — a rendered measurement-plan card showing high/medium/low buckets for a worked priority set).


How it fits in the toolbox

Analytics-Plan Generator is a Product — a thin orchestration over existing spokes, composed strictly over HTTP + vendored contracts, never cross-spoke internal modules. Its distinctive IP is the composition: the priority-relevance router plus VOI ranking that plates the underlying instruments into one deliverable plan.

  • Engine / home: the factor-models spoke. generateAnalyticsPlan in core/analytics-plan.ts is the marquee composite; the contract types live in that spoke's contracts/types.ts (0.3.0). The plan reuses factor-models' own role as the registry of versioned, validated factor models — the candidate models it ranks are exactly the latent-structure models that spoke governs. For how those models are built, validated, and publish-gated, see the factor-models spoke explainer.
  • Composes (inputs supplied by):
    • preference-modeler — the natural source of priorityWeights (forced-choice / penny-allocation elicitation of what leadership values). The Generator takes the numbers; it does not run the elicitation itself.
    • forecasting — the source of the value-of-information inputs (evpi, typicalReduction ≈ EVSI/EVPI, typicalCost). Forecasting owns the EVPI/EVSI machinery and the measurement-method catalog (forecasting.measurement-catalog, forecasting.measurement-recommend); those numbers are computed there and handed in.
    • segmentation-studio (planned composition) — supplies org understanding (which cohorts the priorities apply to). Today the engine is org-agnostic by design — the donor's dept/role/culture coupling was deliberately reimplemented as a generic priority-weight router. Wiring org context back in is (TBD — segmentation-studio composition).
    • metrics-catalog (planned composition) — the menu of available analyses the candidate measures can be drawn from (TBD — metrics-catalog menu binding).
  • Emits: the AnalyticsPlan contract (rankedModels + measurementPlan). Consumers vendor src/spokes/factor-models/contracts/types.ts.
  • Consumers: factor-models lists performix (in-progress) and vela (planned) as consumers in the registry; the Analytics-Plan route is part of that spoke's public surface.

Visual — Tier B (typographic composition map). preference-modeler (priority weights) · forecasting (EVPI/EVSI/cost) · factor-models (candidate models)Analytics-Plan Generatorranked models + VOI-ranked measurement plan → consuming surfaces. The composition direction is one-way: Instruments are the source of truth; the Product consumes them via API.


One load-bearing worked example

This is a real run of the engine, computed from the values fixed in the spoke's own contract-conformance test (src/spokes/factor-models/tests/analytics-plan.test.ts). No number here is invented.

Input — two priorities and one candidate model:

  • priorityWeights: { retention: 60, culture: 40 }
  • candidateModels: [ { modelId: "m1", name: "Attrition CAM", category: "hr", priorityTags: ["retention"], measures: [ { measureId: "flight-risk", evpi: 12, typicalReduction: 0.8, typicalCost: 2 } ] } ]

The arithmetic the engine runs:

  1. Normalize weights: retention 60/100 = 0.6, culture 40/100 = 0.4.
  2. Rank the model: its only tag is retention, so relevance = 0.6. Its category hr matches no high-weight priority, so no bonus. rankedModels = [{ modelId: "m1", name: "Attrition CAM", relevanceScore: 0.6 }].
  3. Score the measure flight-risk: expectedNetVoi = 12 × 0.8 − 2 = 7.6.
  4. Bucket it: cost is 2, and 7.6 > 2 × 2 = 4, so the bucket is high.

OutputmeasurementPlan = [{ modelId: "m1", measureId: "flight-risk", expectedNetVoi: 7.6, priority: "high" }]. The test asserts exactly this priority bucket (assert.equal(plan.measurementPlan[0].priority, "high")).

What a practitioner does with it: the plan says, in one line, run the flight-risk measure on the Attrition CAM first — it is the highest-value, decision-relevant thing you can measure given that retention is the dominant priority. Had flight-risk come back with a negative net VOI (e.g. a cheap-EVPI, expensive-study measure like the test's low-neg case: evpi 1 × 0.1 − 5 < 0), it would land in the low bucket — an explicit signal not to spend on it.


Commercialization / packaging

Analytics-Plan Generator is positioned as one of the toolbox's composed Products (the "meals") that a People Analytics buyer actually meets, sitting above the stateless Instruments (the "ingredients") in the underlying spokes. Its sellable value is the orchestration — translating priorities into a defensible, value-ranked plan — not any single primitive.

  • Data-license posture: the engine itself is content-free — it computes on the priorities and VOI inputs a customer supplies and reads no licensed dataset at compute time. License constraints attach to the inputs (e.g. any vendor-survey-derived measurement catalog upstream in forecasting), handled where those inputs originate, not here.
  • Product-tier placement, pricing, packaged-offering definition: (TBD) — not earned yet, so not stated.

Visual — (TBD — product-tier placement diagram positioning the Product above its composed Instruments).


Current status

Grounded in the real code state (factor-models contract 0.3.0):

  • Shipped: the engine generateAnalyticsPlan (priority-relevance ranking + VOI-ranked measurement plan with high/medium/low bucketing and the negative-VOI don't-measure signal); the full zod contract (AnalyticsPlanRequestSchema, AnalyticsPlanSchema, and the CandidateModel / CandidateMeasure / RankedModel / MeasurementPlanItem types); the live stateless route POST /api/spokes/factor-models/analytics-plan (factor-models.analytics-plan, service-key-gated); a contract-conformance + behavior test suite covering ranking, VOI math, bucket thresholds, ties, and empty/zero-weight handling.
  • In flight / planned: the upstream composition wiring — automatic ingestion of priorityWeights from a preference-modeler elicitation, VOI inputs from forecasting's EVPI/EVSI + measurement catalog, org context from segmentation-studio, and the analyses menu from metrics-catalog — is the orchestration the Product framing implies and is (TBD). Today each composition is done by the caller assembling the request. A buyer-facing surface that renders the plan is (TBD).

Visual — (TBD — live curl capture of the factor-models.analytics-plan route returning the worked-example plan).


The vision

Analytics that produces an analytics plan that produces results — a People Analytics function whose every measurement is anchored to a decision, ranked by what it is worth, and honest about when the right answer is to measure nothing at all.

The direction is to close the loop from stated strategy to measured result: priorities elicited in preference-modeler flow automatically into model relevance, value-of-information from forecasting ranks the plan, the plan drives what gets measured, and the results feed back to re-prioritize. The engine is the spine that is built; the surrounding compositions — and a surface a leadership team can sit in front of — are where it is going.