How folding works
What a hold does with commits while its link's window is full (two messages unacknowledged), and why folding needs no size measuring.
The rule
While the window is full, a hold keeps the ops of each new commit raw in a pending list. Before appending an op, it looks at the last pending op only:
| last pending | new op | result |
|---|---|---|
a path "Hel" | a path "lo" | one a path "Hello" (text joined) |
s path 41 or a path … | s path 42 | one s path 42 (the set overwrites what came before) |
| anything on another path | anything | appended as it is |
Applied in order, the folded list gives the same value as the unfolded one: only neighbours on the same path merge, so no op in between can see the difference. Cost: one path comparison per op. When an ack frees a slot, the pending list is encoded once into one message.
What the four cases show
| case | pending after the stall | why |
|---|---|---|
| streamed reply | 1 op | every token appends to the same path |
| counter, 100,000 sets | 1 op | each set overwrites the previous one |
| reply + status, interleaved | grows | the neighbour is always on the other path: adjacent-only folding cannot merge |
| new list items | grows | inserts carry new data: nothing to fold, this is the real change |
The last two cases are why a memory bound is still needed. It can be a plain count of pending ops after folding (say 1,000): past it the hold drops its list and marks “send the current value”, which then costs nothing more while the stall lasts.
Why not time since the last ack?
A time trigger (“no ack for T seconds: drop the list, send a snapshot later”) punishes the most common case: a phone in a tunnel while a reply streams into a long conversation. Folded, the 60 s stall costs one append of about 12 KB; a snapshot of the conversation costs its whole size (Astra's corpus: 4.6 KB of updates against 168 KB, recovery 0.2 s against 2.7 s). And before T, a fast producer on many paths still piles up rate × T ops: time bounds duration, not memory. With folding, the counter case (100,000 sets) is one op whatever the time, so time adds nothing there.