2026-09-05 — One chunk, from two that disagreed
This came out of a cross-repository review of four applications, not from anything the kernel needed. The review looked for code worth sharing and found almost none: nearly every candidate turned out to be domain logic that belongs to whichever service owns the data. One function survived the test, and this session moves it here.
Why chunk and nothing else
Two applications each carried a private utils/chunk.ts. Both split an array into runs so a database batch stays under
the platform's statement ceiling; both are pure; neither knows what its elements are. That last part is the test the
other candidates failed — a company normaliser and a content-hash both looked shared and both encode a decision about a
specific market's data.
The rule this package already states is the one that was applied: a helper belongs here when it names no vendor, no
runtime and no HTTP framework. chunk names none of the three.
The two implementations disagreed, and only at the boundary
They agreed on everything except a size below one, which each had guarded against because a step of 0 does not
terminate:
- one clamped the step to
1, producing many single-element runs - the other returned the whole input as a single run
The second is the more tempting reading — "no meaningful limit, so no split" — and it is the wrong one. The only promise
the function makes is that no run is longer than size. Returning everything in one run breaks that promise in the
direction that hurts: the caller asked for a bounded batch because something downstream has a ceiling, and it now gets
an unbounded one, which fails at the far end where the ceiling actually lives, in whatever terms that system uses.
List.chunk clamps. A misconfigured 0 becomes many small runs — slow, obvious, recoverable. So does anything else
that is not a finite number of one or more, and NaN is the case that will really happen, since a batch size read from
an environment variable arrives as Number(undefined) when the variable is missing.
A fractional size is truncated rather than passed through, because slice with a step of 2.5 cuts at drifting
boundaries and produces runs of alternating length.
List, not a bare function
Both consumers exported a bare chunk. This package's utilities are classes of static methods — Text, Time,
Signature — so it arrives as List.chunk, and the two call sites change shape when they adopt it. That is a rename in
a consumer, not a behaviour change.
What this does not do
Nothing in the kernel calls List.chunk, and the module note in utils/ claimed everything there existed because the
kernel needed it. That was already untrue of Signature, which no code in src/ calls either. The note now says what
is actually true: these are published because they are pure and vendor-neutral, and a helper already written twice in
two consumers is one those consumers should share rather than keep diverging copies of.
The consumers still carry their own copies. They cannot import this until 1.3.0 is published, and removing a working local helper before its replacement exists on the registry would break both builds for the sake of tidiness.