fix(audit): require integer drift minimum sample thresholds
Enforce non-negative integer minCandidateSampledEvents/minBaselineSampledEvents in drift gate evaluation and CLI parsing; add regression coverage. Architecture/protocol diagrams reviewed and no updates were needed for this validation-only change.
This commit is contained in:
@@ -69,8 +69,8 @@ function usage(): string {
|
||||
' --out <path> Write output to file instead of stdout',
|
||||
'',
|
||||
'Drift thresholds (optional):',
|
||||
' --min-candidate-sampled-events <number>',
|
||||
' --min-baseline-sampled-events <number>',
|
||||
' --min-candidate-sampled-events <integer>',
|
||||
' --min-baseline-sampled-events <integer>',
|
||||
' --max-sampled-events-drop-pct <number>',
|
||||
' --max-run-outcomes-drop-pct <number>',
|
||||
' --max-completion-rate-drop-pp <number>',
|
||||
@@ -106,6 +106,20 @@ function parseOptionalNumber(raw: string | undefined, flag: string): number | un
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function parseOptionalInteger(raw: string | undefined, flag: string): number | undefined {
|
||||
const parsed = parseOptionalNumber(raw, flag);
|
||||
if (parsed === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (!Number.isInteger(parsed)) {
|
||||
throw new Error(`Invalid ${flag} value "${raw}". Expected an integer.`);
|
||||
}
|
||||
if (parsed < 0) {
|
||||
throw new Error(`${flag} must be greater than or equal to 0.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function parseBackends(raw: string | undefined): Phase0BackendTarget[] {
|
||||
const values = parseCsv(raw) ?? ['pi_embedded', 'native'];
|
||||
const parsed: Phase0BackendTarget[] = [];
|
||||
@@ -162,8 +176,8 @@ async function writeOutput(pathValue: string, output: string): Promise<void> {
|
||||
function buildThresholds(values: Record<string, string | boolean | undefined>): Phase0BaselineDriftGateThresholds {
|
||||
return {
|
||||
requireBaselineHistory: Boolean(values['require-baseline-history']),
|
||||
minCandidateSampledEvents: parseOptionalNumber(values['min-candidate-sampled-events'] as string | undefined, '--min-candidate-sampled-events'),
|
||||
minBaselineSampledEvents: parseOptionalNumber(values['min-baseline-sampled-events'] as string | undefined, '--min-baseline-sampled-events'),
|
||||
minCandidateSampledEvents: parseOptionalInteger(values['min-candidate-sampled-events'] as string | undefined, '--min-candidate-sampled-events'),
|
||||
minBaselineSampledEvents: parseOptionalInteger(values['min-baseline-sampled-events'] as string | undefined, '--min-baseline-sampled-events'),
|
||||
maxSampledEventsDropPct: parseOptionalNumber(values['max-sampled-events-drop-pct'] as string | undefined, '--max-sampled-events-drop-pct'),
|
||||
maxRunOutcomesDropPct: parseOptionalNumber(values['max-run-outcomes-drop-pct'] as string | undefined, '--max-run-outcomes-drop-pct'),
|
||||
maxCompletionRateDropPp: parseOptionalNumber(values['max-completion-rate-drop-pp'] as string | undefined, '--max-completion-rate-drop-pp'),
|
||||
|
||||
Reference in New Issue
Block a user