When code must normalize or classify structured data (messages, request bodies, model/provider selection, metrics), implement it as deterministic, idempotent, and non-mutating transformations.

Apply these rules:

Example (non-mutating consecutive-role merge + idempotent ops sketch):

type Content = { role: string; parts: Array<{ text: string }> };

function mergeConsecutiveSameRole(contents: Content[]): Content[] {
  const merged: Content[] = [];
  for (const entry of contents) {
    const last = merged[merged.length - 1];
    if (last && last.role === entry.role) {
      // mutate only the new output copy
      last.parts.push(...entry.parts);
    } else {
      // prevent caller mutation
      merged.push({ ...entry, parts: [...entry.parts] });
    }
  }
  return merged;
}

type Op = { kind: 'prepend_system_block'; text: string; idempotencyKey?: string };
function applyOp(blocks: Array<{ type: 'text'; text: string }>, op: Op) {
  const alreadyThere = blocks.some((b) => b.text === op.text || b.text.startsWith(op.text));
  return alreadyThere ? blocks : [{ type: 'text', text: op.text }, ...blocks];
}

Practical checklist: