ruvnet/ruflo · error · RangeError
testIndex must be a positive integer
Error message
testIndex must be a positive integer
What it means
Thrown by alphaForTest() as a RangeError when testIndex is not a positive integer. testIndex is the 1-based position of this candidate in the lineage's sequential test stream; it determines the alpha share (alphaTotal * 6/(pi^2 * k^2)). Zero, negative, or fractional indices are meaningless for the family-wise error budget. Called transitively by sequentialEvidenceVerdict, minInformativePairsToClear, and remainingAlphaBudget.
Source
Thrown at v3/@claude-flow/cli/src/services/flywheel-sequential-evidence.ts:79
export interface SequentialEvidenceVerdict {
significant: boolean;
eValue: number;
threshold: number; // 1 / alphaAllocated
alphaAllocated: number; // this test's share of the family-wise budget
testIndex: number; // 1-based position in the lineage's test stream
informativePairs: number; // discordant pairs — the only ones carrying signal
totalPairs: number;
version: typeof SEQUENTIAL_EVIDENCE_VERSION;
}
/**
* Alpha share for the k-th test in the stream: alphaTotal * 6/(pi^2 k^2).
* Chosen over 2^-k because it decays polynomially — test 10 still gets a
* workable ~0.6% of a 5% budget instead of ~0.005%.
*/
export function alphaForTest(testIndex: number, alphaTotal = DEFAULT_ALPHA_TOTAL): number {
if (!Number.isInteger(testIndex) || testIndex < 1) throw new RangeError('testIndex must be a positive integer');
if (!(alphaTotal > 0 && alphaTotal < 1)) throw new RangeError('alphaTotal must be in (0, 1)');
return (alphaTotal * 6) / (Math.PI * Math.PI * testIndex * testIndex);
}
/**
* Fold paired outcomes into an anytime-valid e-value and judge it against the
* k-th test's allocated alpha. Deterministic; order of outcomes does not
* change the final e-value (the product commutes).
*/
export function sequentialEvidenceVerdict(
outcomes: PairedTaskOutcome[],
testIndex: number,
config: SequentialEvidenceConfig = {},
): SequentialEvidenceVerdict {
const alphaTotal = config.alphaTotal ?? DEFAULT_ALPHA_TOTAL;
const lambda = config.lambda ?? DEFAULT_LAMBDA;
const epsilon = config.epsilon ?? DEFAULT_SCORE_EPSILON;
if (!(lambda > 0 && lambda < 1)) throw new RangeError('lambda must be in (0, 1)');View on GitHub (pinned to 6b01dc5a68)
Solutions
- Use a 1-based index: testIndex = Object.keys(state.sequentialTests).length + 1 (the transaction layer allocates this).
- Validate before calling: if (!Number.isInteger(k) || k < 1) throw.
- Let the flywheel-transaction promotion path allocate the index rather than computing it manually.
Example fix
// before const k = state.sequentialTests?.[receiptId] ?? 0; // 0-based, will throw sequentialEvidenceVerdict(outcomes, k); // after const k = nextTestIndex(state); // returns 1-based positive integer sequentialEvidenceVerdict(outcomes, k);
Defensive patterns
Strategy: validation
Validate before calling
function assertPositiveInt(x: number, name: string): void {
if (!Number.isInteger(x) || x < 1) throw new RangeError(`${name} must be a positive integer, got ${x}`);
}
assertPositiveInt(testIndex, 'testIndex');
alphaForTest(testIndex); Type guard
const isPositiveInt = (x: unknown): x is number => typeof x === 'number' && Number.isInteger(x) && x >= 1;
Prevention
- Always allocate testIndex from the transaction ledger (1-based), never compute it ad hoc.
- Convert 0-based array indices to 1-based before passing.
- Unit-test the sequential evidence functions with k=1 as the minimum.
When it happens
Trigger: Calling alphaForTest(0), alphaForTest(-1), alphaForTest(1.5), or sequentialEvidenceVerdict(outcomes, 0). Also if a caller computes testIndex from an empty/zero-based ledger without converting to 1-based.
Common situations: A transaction state with zero prior tests where the caller passes the raw length (0) instead of length+1; a 0-based array index used directly; an uninitialised sequentialTests ledger.
Related errors
- lambda must be in (0, 1)
- testsRun must be a non-negative integer
- metric must be finite
- bootstrap iterations must be >= 100
- unknown game "${key}". Known: ${Object.keys(GAMES).join(', '
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/c11c8fa2a31580a3.
Report an issue: GitHub.