File size: 2,558 Bytes
9646e24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { CommitmentConfidenceBreakdown, CommitmentMetrics } from "@/types";

function clamp01(value: number): number {
  if (!Number.isFinite(value)) return 0;
  return Math.max(0, Math.min(1, value));
}

function rate(success: number, total: number, priorAlpha: number, priorBeta: number): number {
  if (!Number.isFinite(success) || !Number.isFinite(total)) return clamp01(priorAlpha / (priorAlpha + priorBeta));
  const s = Math.max(0, success);
  const t = Math.max(0, total);
  const denom = t + priorAlpha + priorBeta;
  if (denom <= 0) return 0;
  return clamp01((s + priorAlpha) / denom);
}

/**
 * Convert historical outcomes into a calibrated probability breakdown.
 *
 * This is intentionally simple and fully auditable:
 * - each component is an empirical success rate
 * - overall is multiplicative, matching the spec
 */
export function computeConfidenceBreakdown(metrics: CommitmentMetrics): CommitmentConfidenceBreakdown {
  const capability = rate(metrics.capability.success, metrics.capability.total, 9, 1);
  const inputsAvailable = rate(metrics.inputsAvailable.success, metrics.inputsAvailable.total, 9, 1);
  const toolCompletion = rate(metrics.toolCompletion.success, metrics.toolCompletion.total, 9, 1);
  const qualityApproval = rate(metrics.qualityApproval.success, metrics.qualityApproval.total, 9, 1);
  const acceptedWithoutRevision = rate(
    metrics.acceptedWithoutRevision.success,
    metrics.acceptedWithoutRevision.total,
    7,
    3,
  );

  const overall = clamp01(
    capability *
      inputsAvailable *
      toolCompletion *
      qualityApproval *
      acceptedWithoutRevision,
  );

  return {
    capability,
    inputsAvailable,
    toolCompletion,
    qualityApproval,
    acceptedWithoutRevision,
    overall,
  };
}

function percentile(sorted: number[], p: number): number {
  if (sorted.length === 0) return 0;
  const idx = (sorted.length - 1) * p;
  const lo = Math.floor(idx);
  const hi = Math.ceil(idx);
  if (lo === hi) return sorted[lo];
  const weight = idx - lo;
  return sorted[lo] * (1 - weight) + sorted[hi] * weight;
}

export function estimateCompletionTimes(metrics: CommitmentMetrics): {
  p50Ms: number | null;
  p90Ms: number | null;
} {
  const durations = Array.isArray(metrics.durationsMs) ? metrics.durationsMs.filter((d) => d > 0) : [];
  if (durations.length < 5) {
    return { p50Ms: null, p90Ms: null };
  }

  const sorted = [...durations].sort((a, b) => a - b);
  return {
    p50Ms: Math.round(percentile(sorted, 0.5)),
    p90Ms: Math.round(percentile(sorted, 0.9)),
  };
}