Methodology functions
These are the implementations. There is exactly one copy of each, in /packages/shared.
P1, weighted value, ratio
Section titled “P1, weighted value, ratio”import { assertNever, type Ladder, type Step } from "./types.js";
/** * P1 is locked to step. Changing P1 is a step transition. * Step 7 is closed; P1 is not applicable. */export function p1FromStep(step: Step): number | null { switch (step) { case 1: case 2: return 30; case 3: case 4: return 60; case 5: case 6: return 90; case 7: return null; default: { const _exhaustive: never = step; return assertNever(_exhaustive); } }}
/** * Resolve a proposed P1 ladder value to the target step for an upward * (or same-band) move. The agent speaks in probabilities; middleware uses steps. */export function stepFromP1(p1: Ladder, currentStep: Step): Step { switch (p1) { case 30: return currentStep <= 2 ? currentStep : 2; case 60: return currentStep <= 4 ? Math.max(currentStep, 3) as Step : 4; case 90: return currentStep <= 6 ? Math.max(currentStep, 5) as Step : 6; default: { const _exhaustive: never = p1; return assertNever(_exhaustive); } }}
/** * weighted = (P1/100) * (P2/100) * amount * Ceiling is 81% (90*90). Never stored. Never normalised. */export function weightedValue( p1: number | null, p2: number, amount: number,): number { if (p1 === null) { return 0; } return (p1 / 100) * (p2 / 100) * amount;}
export function ratioPercent(weightedTotal: number, unweightedTotal: number): number { if (unweightedTotal === 0) { return 0; } return (weightedTotal / unweightedTotal) * 100;}
export function isOpenPipeline(step: Step): boolean { return step < 7;}import { missingRequiredCriteria } from "./evidence.js";import type { MeddicCriterion, MeddicFields, RagRating } from "./types.js";
export type RagBands = { greenMaxMissing: number; amberMaxMissing: number;};
export const DEFAULT_RAG_BANDS: RagBands = { greenMaxMissing: 0, amberMaxMissing: 2,};
/** * RAG is derived only from MEDDIC completeness against the current step's * required criteria. Flags (rule or judgement) never feed it. */export function ragFromMeddic( fields: MeddicFields, requiredAtStep: MeddicCriterion[], bands: RagBands = DEFAULT_RAG_BANDS,): RagRating { const missing = missingRequiredCriteria(fields, requiredAtStep).length; if (missing <= bands.greenMaxMissing) { return "green"; } if (missing <= bands.amberMaxMissing) { return "amber"; } return "red";}