uint8_t rb_push(ring_t *rb, uint8_t byte) {- if (rb->head == rb->tail) return 0;+ if (rb_full(rb)) return 0; rb->buf[rb->head] = byte; rb->head = (rb->head + 1) % rb->size;You: The full-vs-empty check still uses head == tail. The bug moved behind rb_full().
Model review: Confirmed. rb_full() needs a count field, not only head and tail.
Review verdict: Smaller diff, same bug. The implementation changed; the invariant did not.
Open this PR in the demo