Splitting a list without breaking the procedure
A user asks how to rotate an API key and gets back three steps out of nine, starting at step 4. They follow them. It does not work, and they have no way to know that the missing steps existed.
Lists look like prose to a splitter — lines of text separated by newlines — and they are not. A list is a set of items with a relationship to each other, and the relationship is the part that gets cut away.
Three kinds of list, three relationships
Ordered procedures. Steps 1 through N, where the order is the meaning. Item 7 presupposes items 1 through 6. Truncating a procedure is the most harmful list failure because the result is actionable and wrong.
Heterogeneous bullets. A list of independent points under one heading — supported formats, known limitations, required permissions. Order carries little; the items are siblings. Each is nearly self-contained but only makes sense under its introducing line.
Definition and mapping lists. Term/description pairs, error-code tables written as lists, glossary entries. Each item is a record. These are the easiest to handle and the ones most often mangled by size limits, because a long glossary blows through any chunk size.
Deciding which one you have is the whole job. The same markdown syntax expresses all three.
The split, shown
A nine-step procedure, cut at a size limit, then cut with the list treated as a unit.
=== SIZE-DRIVEN CUT ===
--- chunk 1 ---
## Rotating an API key
Rotation creates a new key and revokes the old one. Both keys
are valid during the overlap period.
1. In the dashboard, open Settings → API keys.
2. Select the key to rotate and choose Rotate.
3. Copy the new key. It is shown once.
4. Set the overlap period. The default is 24 hours.
--- chunk 2 ---
5. Deploy the new key to every service that authenticates
with it.
6. Confirm in the dashboard that the old key shows zero
requests in the last hour.
7. Then revoke the old key manually, or wait for the overlap
period to expire.
8. Verify that all services are still authenticating.
9. Delete the stored copy of the old key.
Chunk 2 is a fragment of a procedure that does not say what procedure it is, does not say the word “rotate,” and begins at step 5 with no indication that steps 1 to 4 exist. Its most retrievable sentence is step 7 — “Then revoke the old key manually” — which is the single most destructive thing in the document to hand somebody out of order.
=== LIST TREATED AS A UNIT ===
--- chunk 1 ---
## Rotating an API key
Rotation creates a new key and revokes the old one. Both keys
are valid during the overlap period.
[procedure: 9 steps — see chunk 2]
--- chunk 2 ---
## Rotating an API key (procedure, steps 1–9 of 9)
Rotation creates a new key and revokes the old one. Both keys
are valid during the overlap period.
1. In the dashboard, open Settings → API keys.
... (all nine steps) ...
9. Delete the stored copy of the old key.
And if the procedure genuinely does not fit, the fallback is not a size-driven cut but a labelled one:
--- chunk 2b ---
## Rotating an API key (procedure, steps 5–9 of 9)
Preceding steps: open Settings → API keys, choose Rotate,
copy the new key, set the overlap period.
5. Deploy the new key to every service that authenticates
with it.
... (through step 9) ...
The fragment now names the procedure, states its position in it, and summarises what came before. It is retrievable for “rotate API key” and it cannot be mistaken for the whole thing.
The rules
1. Detect list boundaries before applying size limits. A list starts at its introducing line — the sentence ending in a colon, or the heading above it — and ends at the last item. That whole span is a candidate atomic unit, the same way a table is.
2. Keep the introducing line with the items, always. “The following permissions are required:” is the only text in the region that says what the list is. Separating it from the items is the most common list failure after truncation, and it is free to fix.
3. Never split an ordered procedure if you can avoid it. Prefer exceeding your nominal chunk size to splitting a nine-step process, within whatever headroom your embedding model’s input limit allows. A procedure is one idea expressed in numbered pieces.
4. When you must split a procedure, label the fragment. Position (“steps 5–9 of 9”), the procedure’s name, and a one-line summary of the preceding steps. Store the neighbour IDs so the rest can be fetched.
5. Split heterogeneous bullet lists at item boundaries, never inside an item. These tolerate splitting far better than procedures because the items are siblings. Repeat the heading and the introducing line on each fragment, exactly as you would repeat a table’s header row.
6. Consider one chunk per item for definition lists. Where each item is a self-contained record — an error code and its meaning, a term and its definition — a chunk per item gives you sharp vectors and precise citations. Prefix each with the list’s heading so it knows what family it belongs to. This is the same argument that makes pre-split documents worth leaving alone.
7. Respect nesting. A sub-list belongs to its parent item. Cutting between “3.” and “3a.” produces a fragment beginning with a lettered item and no idea what it qualifies.
What still breaks
Interleaved prose. Real procedures have explanatory paragraphs between steps, warnings, and sometimes a code block per step. The list span is then most of the section, and “keep it whole” turns into “keep an entire section whole.” Sometimes the right answer is that the section is the chunk.
Very long lists. A 300-entry error-code list cannot be one chunk. Per-item chunking is usually right here, and it produces hundreds of small, similar chunks that will crowd each other in any result set — a real cost, honestly incurred.
Lists whose items reference each other. “Repeat step 4 for each region.” Splitting anywhere breaks it, and even keeping the list whole does not help if step 4 was in a different list. Same class of problem as any other cross-reference.
Lists that are secretly tables. Three-column data written as bullets with dashes. Detecting them is harder than detecting a table, because there is no syntax to look for.
Lists that are secretly headings. A table of contents, or a navigation list. These are list-shaped and carry no content; indexing them produces chunks that match many queries and answer none.
Semantic splitters and lists do not mix. Consecutive items in a heterogeneous list are dissimilar by construction, so similarity-based splitting will cut between every pair. Route lists away from that stage.
How to tell if it worked
Query your index for the imperative verbs your procedures start with — “rotate,” “configure,” “install,” “revoke” — and look at what comes back. If you get mid-procedure fragments, you have the failure at the top of this post.
Then run a direct check over the whole index: for every chunk whose first line matches a numbered-list item pattern with a number greater than one, flag it. Each flag is a procedure that got cut, and the list of flags is short enough to read. That single query finds more real damage than any aggregate score, and it takes about ten minutes to write.