{"record":{"id":"02deebf5cabaae93","repo":"ruvnet/ruflo","slug":"testsrun-must-be-a-non-negative-integer","errorCode":null,"errorMessage":"testsRun must be a non-negative integer","messagePattern":"testsRun must be a non-negative integer","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/flywheel-sequential-evidence.ts","lineNumber":142,"sourceCode":"/**\n * Minimum number of INFORMATIVE (discordant) pairs a candidate must win —\n * with zero losses — to clear the k-th test's threshold: the smallest n with\n * (1+lambda)^n >= 1/alpha_k. The pre-flight power check (ADR-381 §4): an\n * evaluation whose promotion holdout is smaller than this cannot promote even\n * on a perfect sweep, so it should be refused BEFORE compute is spent and\n * before a doomed receipt can be presented to the gate (spending alpha).\n */\nexport function minInformativePairsToClear(testIndex: number, config: SequentialEvidenceConfig = {}): number {\n  const alphaTotal = config.alphaTotal ?? DEFAULT_ALPHA_TOTAL;\n  const lambda = config.lambda ?? DEFAULT_LAMBDA;\n  if (!(lambda > 0 && lambda < 1)) throw new RangeError('lambda must be in (0, 1)');\n  const threshold = 1 / alphaForTest(testIndex, alphaTotal);\n  return Math.ceil(Math.log(threshold) / Math.log(1 + lambda));\n}\n\n/** Family-wise budget left after `testsRun` allocated tests: alphaTotal - Σ alpha_k. */\nexport function remainingAlphaBudget(testsRun: number, alphaTotal = DEFAULT_ALPHA_TOTAL): number {\n  if (!Number.isInteger(testsRun) || testsRun < 0) throw new RangeError('testsRun must be a non-negative integer');\n  let spent = 0;\n  for (let k = 1; k <= testsRun; k++) spent += alphaForTest(k, alphaTotal);\n  return Math.max(0, alphaTotal - spent);\n}\n\nexport interface PairedEvidenceCheck {\n  ok: boolean;\n  reasons: string[];\n}\n\n/**\n * Structural consistency between a receipt's paired outcomes and its\n * aggregate heldOutDeltas: same length and order, unique non-empty task IDs,\n * and each delta must equal candidateScore - baselineScore. This is what makes\n * paired outcomes EVIDENCE rather than decoration — an aggregate that cannot\n * be reproduced from its own per-task rows is refused.\n */\nexport function checkPairedOutcomesConsistency(","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/flywheel-sequential-evidence.ts#L124-L160","documentation":"Thrown by remainingAlphaBudget() as a RangeError when testsRun is not a non-negative integer. remainingAlphaBudget computes how much of the family-wise alphaTotal budget remains after k allocated tests (alphaTotal - sum of alpha_k). Negative or fractional counts are meaningless for the spend ledger.","triggerScenarios":"Calling remainingAlphaBudget(-1), remainingAlphaBudget(2.5), or passing a non-integer count derived from a float computation or an uninitialised field.","commonSituations":"A UI/CLI that passes the raw sequentialTests map size before validating it's a non-negative integer; a default value of -1 used as a sentinel that leaked into the call.","solutions":["Ensure testsRun is a non-negative integer: Math.max(0, Math.floor(n)).","Default to 0 when the ledger is empty/uninitialised.","Validate with Number.isInteger(testsRun) && testsRun >= 0 before calling."],"exampleFix":"// before\nremainingAlphaBudget(state.sequentialTests ? Object.keys(state.sequentialTests).length : -1);\n// after\nremainingAlphaBudget(state.sequentialTests ? Object.keys(state.sequentialTests).length : 0);","handlingStrategy":"validation","validationCode":"if (!Number.isInteger(testsRun) || testsRun < 0) {\n  throw new RangeError(`testsRun must be a non-negative integer, got ${testsRun}`);\n}","typeGuard":"const isNonNegativeInt = (x: unknown): x is number => typeof x === 'number' && Number.isInteger(x) && x >= 0;","tryCatchPattern":null,"preventionTips":["Default testsRun to 0 when the ledger is empty rather than using -1 sentinels.","Floor any computed counts before passing: Math.max(0, Math.floor(n)).","Derive testsRun from Object.keys(state.sequentialTests ?? {}).length."],"tags":["validation","statistics","sequential-evidence","range-error"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}