Custom collections (ring buffer, skip list, B-tree, linked list, sparse array) expose their internal representation to consumers who want to walk them. Every traversal site duplicates the internal-structure knowledge; refactoring the collection's representation breaks every consumer in lockstep.
Per-traversal duplication of representation-specific logic the agent must verify uniformly on every collection-internals change. Insider Trading on collection internals couples every consumer to the collection's private fields; refactoring the collection's storage is N-files-in-lockstep.
The collection exposes one iterator-shaped surface (next() / done); consumers use it without knowing the underlying representation. Replacing the representation (array-backed to linked-list-backed) is a one-file change inside the collection.
One iterator protocol the agent reads to understand traversal semantics. Consumer code is uniform `for (const x of coll)` — the agent's reasoning about traversal collapses to one shape; representation changes inside the collection scope to one file.
Before the pattern
class RingBuffer {constructor(capacity) {this.buffer = new Array(capacity);this.head = 0;this.tail = 0;this.size = 0;this.capacity = capacity;}push(item) { /* ... */ }}function dump(buf) {let i = buf.head;let count = 0;while (count < buf.size) {console.log(buf.buffer[i]);i = (i + 1) % buf.capacity;count++;}}// Every consumer that wants to iterate must know head/tail/capacity// and the modular arithmetic.
After the pattern
class RingBuffer {constructor(capacity) {this.buffer = new Array(capacity);this.head = 0;this.tail = 0;this.size = 0;this.capacity = capacity;}push(item) { /* ... */ }[Symbol.iterator]() {let i = this.head;let count = 0;const that = this;return {next() {if (count >= that.size) return { done: true, value: undefined };const value = that.buffer[i];i = (i + 1) % that.capacity;count++;return { done: false, value };},};}}for (const item of ringBuffer) {console.log(item);}
Per-consumer traversal logic is Insider Trading on the collection's internals — Message Chains (`buf.buffer[buf.head]`) that walk through fields the collection should encapsulate. Changing head/tail semantics or storage layout forces editing every consumer.
Per-consumer traversal logic forces the agent to enumerate every consumer on every storage-layout change to prove the change is safe; the verification cost scales with the consumer count, and partial verification ships silent bugs that survive review.
Iterators allocate per-traversal (an iterator object per for-of loop); in tight inner loops this matters. They also obscure performance characteristics — a consumer cannot tell from `for (const x of collection)` whether the iterator is O(1) per step or O(log n).
Per-traversal iterator allocation and the closure semantics of [Symbol.iterator] hide performance characteristics from the agent's static read. Tight-loop performance bugs require the agent to look at the iterator implementation, which is hidden behind the protocol.
Consumers read as `for (const x of collection)`; the collection's representation changes without touching consumers; performance and correctness optimizations on the iteration live in one place; the language's iteration protocols (for-of, spread, destructuring) work out of the box.
Consumer-side traversal is one expression; collection-side representation is one file; agent edits to storage shape scope to one place and need no cross-consumer verification. Static analysis of 'where is this collection iterated' returns complete results.
Iterators that mutate the collection underneath (allow .push() during iteration, or invalidate on .delete()) produce inconsistent traversal behaviour that no test exercises completely. Either freeze the collection during iteration or document mutation semantics; ad-hoc behaviour is a footgun.
Iterators with hidden mutability (re-entrant push during iteration, snapshot-vs-live semantics) produce bugs the agent cannot localize from the iterator protocol alone. The agent reading `for (const x of coll)` trusts the consumer side; runtime ordering bugs ship from the iterator's interaction with mutation, unseen at the call site.