Skip to content

Methodology functions

These are the implementations. There is exactly one copy of each, in /packages/shared.

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";
}