Decision Wizard — plain-language explainer

Decision Wizard walks you through a hard, multi-criteria choice the disciplined way — weigh what matters, score each option under two futures, then find out whether more information is worth buying before you commit.

A People Analytics Toolbox consumer surface (PAT-22). It is a surface, not a spoke: it owns the wizard experience and the decision's shape, but the math lives in the spokes it calls over HTTP. Built to the portfolio Explainer Standard v1.0. Every claim below is grounded in the real surface code (src/surfaces/decision-wizard/, page routes under src/app/(surfaces)/decision-wizard/) and the contracts it consumes — forecasting 1.4.0 and calculus 1.13.0. Anything not yet built is marked (TBD).


Manifesto — why it exists

Most consequential people-decisions — which reorg to run, which vendor to sign, which of two roles to backfill first — get made one of two bad ways. Either someone argues from a single gut number ("Option B feels stronger"), or a spreadsheet quietly launders a pile of subjective scores into one confident-looking total with no sense of how fragile that total is.

The Kepner–Tregoe method has been the disciplined antidote for decades: name the criteria, weight them, score each alternative, and reason explicitly about the uncertainty rather than averaging it away. Decision Wizard is that method made into a guided flow — and then it adds the move most decision tools skip entirely: before you commit, it asks whether buying more information is worth it.

The differentiation beat, stated as a shift:

  • FROM a single weighted total that hides which future it assumed, and offers no answer to "should we just decide now, or go get more data first?"
  • TO an expected value computed across two explicit world states, plus a value-of-information number that puts a dollar-equivalent ceiling on what a perfect forecast would be worth — so "let's commission a study" becomes a quantified call, not a reflex.

How it differs from the obvious substitutes:

  • vs. a weighted-scoring spreadsheet — a spreadsheet gives you one number. The Wizard gives you the optimal decision under the prior, the expected value if you knew the future perfectly, and the gap between them (the EVPI). The gap is the part a spreadsheet structurally cannot show you.
  • vs. generic BI — BI dashboards report what happened; the Wizard structures a forward choice and prices the uncertainty inside it.
  • vs. doing K–T by hand — by hand, the value-of-information step is the one people drop because the arithmetic (rollback expected value, perfect-information value, optional Bayesian signal updating) is fiddly and easy to get wrong. The Wizard hands that arithmetic to the forecasting spoke, which owns it as a tested contract.

Visual — Tier B (FROM→TO typographic block). The shift above is the visual: single hidden weighted totalEV-under-prior + perfect-information EV + EVPI gap, with optional signal value (EVSI). A rendered comparison block is a follow-up (FU-A).

Walkthrough — how it actually works

The buyer meets a six-step wizard at /decision-wizard/[id] (the home route /decision-wizard lists saved sessions and starts new ones). The step order is fixed in code (WIZARD_STEPS in lib/decision-types.ts): Define → Criteria → Alternatives → Score → VOI → Sensitivity → Recommendation. Each step writes back into one DecisionSession object; the surface owns that shape and persists it.

The journey, concretely:

  1. Define — state the decision and a problem statement. The title gates progress (you cannot advance with an empty title).
  2. Criteria — list what matters and give each a weight (e.g. Strategic fit at 1.0, Cost / effort at 0.8). Weights need not sum to one; the surface normalizes by their total when it computes payoffs (weightedCriterionPayoff in lib/build-tree.ts).
  3. Alternatives — name the options. The flow requires at least two before scoring.
  4. Score — rate every alternative on every criterion, twice: once under a favorable world state and once under a stressed one. This is the K–T discipline that forces you to confront that the "best" option can flip depending on which future arrives. The score matrix is two aligned tables (scores.up, scores.down).
  5. VOI — set the two state labels (e.g. Favorable / Stressed), the probability of the favorable state (the other is computed as 1 − p), and a shared uncertainty id. That id is load-bearing: it is passed through to the forecasting contract's sharedUncertaintyId, which is what lets the two chance branches align by index so an honest EVPI can be computed. Optionally, toggle on a discrete information signal (imperfect observations like "Signal: optimistic" with likelihoods under each state) to also get an EVSI.
  6. Sensitivity — an optional Monte Carlo pass: the surface treats the leading alternative's two-state payoff as a discrete-empirical distribution and asks the forecasting spoke to run trials, returning a mean and p50/p90.
  7. Recommendation — the assembled answer: the optimal decision label, the per-alternative confidence summaries, and the VOI verdict.

Where the math actually happens — three real calls, all made from Server Actions (actions.ts) so the TOOLBOX_SERVICE_KEY never reaches the browser:

  • Forecasting · VOI. runVoiAnalysisAction builds a decision tree from the session (sessionToDecisionTree) and POSTs it to /api/spokes/forecasting/voi/compute. The tree is a decision node whose branches are the alternatives, each leading to a chance node carrying the same sharedUncertaintyId and the two state-weighted payoffs. The spoke returns { baselineEV, perfectInformationEV, evpi, evsi?, recommendation }.
  • Calculus · enrichment. enrichWinningAlternativeAction wraps each alternative's mean payoff in a MetricEnvelope (the calculus canonical metric shape) and POSTs to /api/spokes/calculus/stats/enrich, getting back a confidence interval and z-score for context on the weighted summaries.
  • Forecasting · Monte Carlo (optional). runMonteCarloSensitivityAction POSTs to /api/spokes/forecasting/monte-carlo/run for the sensitivity step.

Differentiation beat in the flow: the Wizard never invents a number it cannot defend. The decision tree's rollback EV, the perfect-information EV, and the EVPI are all computed by the forecasting spoke under its versioned contract; the surface only duplicates the rollback EV locally (lib/decision-ev.ts) to label the winner without a round trip, and that local copy is deliberately a vendored re-implementation of the spoke's core/decision-tree.ts rather than a cross-spoke core/ import.

Visual — Tier B (step-flow block). Define → Criteria → Alternatives → Score (×2 world states) → VOI [forecasting] → Sensitivity [Monte Carlo] → Recommendation [+ calculus enrichment]. The bracketed labels mark where each spoke call fires.

What it enables

Concrete decisions a practitioner would bring to it:

  • Reorg or operating-model choice — score two structures on strategic-fit, cost, and execution-risk under a favorable vs. stressed business climate, and see whether the winner is robust or climate-dependent.
  • Vendor / build-vs-buy selection — weight the criteria that actually matter, then get an EVPI that says how much a pilot or reference-check is worth before signing.
  • Backfill / hiring sequencing — which of two open roles to fill first, scored under "headcount holds" vs. "budget tightens."
  • Program go / no-go — pair a structured payoff with an EVSI on whether running a small study first changes the call enough to justify the delay.
  • Pricing or comp-strategy fork — a "several scenarios, never one option" companion to the AnyComp decision layer, for the upstream framing choice rather than the optimizer run.
  • Any defensible two-future bet — anywhere a leader has to choose under uncertainty and wants the choice recorded with its assumptions, its weights, and its information-value ceiling, not just its conclusion.

Visual — Tier A (live capture of the VOI verdict). See the worked example below — a real forecasting-spoke response computed from the surface's own default session.

How it fits in the toolbox

Decision Wizard is a surface under src/surfaces/decision-wizard/, with page routes under src/app/(surfaces)/decision-wizard/. It obeys the surface rule: it composes spokes over HTTP + contracts only, never importing another spoke's core/, db/, or route modules.

  • Composes — forecasting (1.4.0). POST /voi/compute (the EVPI / optional EVSI engine over aligned discrete world states) and POST /monte-carlo/run (sensitivity). The decision-tree types (DecisionNode, ChanceBranch) are imported from @/spokes/forecasting/contracts/types. See the forecasting spoke explainer for the value-of-information math itself.
  • Composes — calculus (1.13.0). POST /stats/enrich for confidence-interval and z-score context, via the canonical MetricEnvelope contract from @/spokes/calculus/contracts/types. See the calculus spoke explainer for the enrichment primitives.
  • Companion surface — Metric Market (/metric-market, PAT-21), linked from the home page. They are siblings: Metric Market is the card workbench; Decision Wizard is the K–T + VOI flow.
  • Tenant boundary (TBD). Spoke calls currently pass tenantId: "__internal_demo__" (actions.ts); real org-context binding is gated on PAT-39-FU-D and noted in code.

Visual — Tier B (typographic data-flow). DecisionSession (surface-owned) → [forecasting voi/compute · monte-carlo/run] + [calculus stats/enrich] → Recommendation. The surface owns the session shape and UX; every number on the recommendation traces to a spoke contract.

One load-bearing worked example

The surface ships a default session (lib/default-session.ts) — two criteria, two alternatives, two world states — so the flow is runnable end to end without entering anything. Running it through the real forecasting math produces a concrete, defensible verdict.

The inputs (verbatim from the default session):

  • Criteria: Strategic fit (weight 1.0), Cost / effort (weight 0.8).
  • Alternative A scores — Favorable: fit 8, cost 7; Stressed: fit 5, cost 6.
  • Alternative B scores — Favorable: fit 6, cost 8; Stressed: fit 7, cost 7.
  • World states: Favorable at 0.55, Stressed at 0.45; shared uncertainty id market.

The surface normalizes by total weight (1.8), giving weighted payoffs: A = 7.556 favorable / 5.444 stressed; B = 6.889 favorable / 7.000 stressed. The decision tree built from these (sessionToDecisionTree) is what POST /voi/compute receives, and it returns:

POST /api/spokes/forecasting/voi/compute
{ "tenantId": "__internal_demo__",
  "label": "Untitled decision",
  "tree": { "kind": "decision", "branches": [
    { "label": "Alternative A", "child": { "kind": "chance", "sharedUncertaintyId": "market",
      "branches": [ { "probability": 0.55, "payoff": 7.5556 }, { "probability": 0.45, "payoff": 5.4444 } ] } },
    { "label": "Alternative B", "child": { "kind": "chance", "sharedUncertaintyId": "market",
      "branches": [ { "probability": 0.55, "payoff": 6.8889 }, { "probability": 0.45, "payoff": 7.0000 } ] } }
  ] } }
→ { "analysis": {
      "baselineEV": 6.9389,
      "perfectInformationEV": 7.3056,
      "evpi": 0.3667,
      "recommendation": "..."
  } }

(Real values: payoffs computed by the surface's weightedCriterionPayoff; the EVs and EVPI computed by the forecasting spoke's rollback. baselineEV = the prior-optimal alternative's expected value — here Alternative B at 6.94, which edges out A's 6.61 only because it holds up in the stressed state. perfectInformationEV = 7.31, the value if you knew which world you were in before choosing. evpi = 0.37, the gap.)

What a practitioner does with it: the prior says pick B — but the EVPI of 0.37 against a baseline of 6.94 is small (~5%), which means the two options are close and resolving the market uncertainty would barely move the decision. The verdict is "decide now; don't pay much to forecast the market first." Had the EVPI come back large, the same flow would have said the opposite — go buy the information. That is the whole point: the recommendation is conditioned on the value of information, not just the point scores.

Commercialization / packaging

Decision Wizard is a toolbox-internal consumer surface — one of the flagship surfaces that demonstrates what the spokes do when composed, not a separately licensed product. It is the buyer-facing front door to the forecasting value-of-information capability and the calculus enrichment primitives.

  • Data-license posture: there is none to manage — the surface holds no external data. It computes entirely from operator-entered criteria, scores, and probabilities, plus the spoke math. Sessions persist to browser localStorage only (PAT-22 V0); there is no multi-user store yet.
  • Anything about pricing tiers or a packaged offering is (TBD) — not earned yet, so not stated.

Visual — (TBD — product-tier placement diagram once the surface's packaging is defined).

Current status

Grounded in the real code state (src/surfaces/decision-wizard/; forecasting 1.4.0, calculus 1.13.0):

  • Shipped: the full six-step wizard UX (Define → Criteria → Alternatives → Score → VOI → Sensitivity → Recommendation) at /decision-wizard and /decision-wizard/[id]; live composition of forecasting voi/compute (EVPI + optional EVSI via discrete signal), forecasting monte-carlo/run (sensitivity), and calculus stats/enrich (CI + z-score); a runnable default session; service-key injection from Server Actions; localStorage session index.
  • In flight / deferred (PAT-22-FU): Postgres persistence + multi-user (sessions are single-browser today); tenant-context binding (__internal_demo__ placeholder, gated on PAT-39-FU-D); a calibration engine; PDF export; real-time co-editing.

Visual — Tier B (in-repo reference). The step set is enumerated in lib/decision-types.ts (WIZARD_STEPS); the spoke calls are the three Server Actions in actions.ts.

The vision

A decision desk where every consequential people-choice is recorded with its criteria, its two futures, and the price it would pay for certainty — so leaders argue about the right things and stop paying for forecasts that wouldn't change the call.

The near-term path moves the surface off localStorage and onto a real persisted, tenant-bound store so decisions become a durable, auditable record rather than a single-browser scratchpad — and binds it into the same org context as the AnyComp decision layer, its sibling in the "several scenarios, never one option" posture. Further out, the calibration engine closes the loop: comparing the world state that actually arrived against the probabilities entered, so the next decision's priors are better than the last one's.


The worked example above is the real output of the forecasting spoke's value-of-information rollback against the surface's own default session (lib/default-session.ts): Alternative B wins under the prior at EV 6.94, perfect information would be worth 7.31, EVPI 0.37 — a small gap that says decide now. No figure here is invented; the payoffs are computed by weightedCriterionPayoff and the expected values by the forecasting contract's tested rollback.