Repository navigation
v0.7.0
Fixed (breaking)
A record could be dropped from a bootstrap page. PHP compares two numeric strings numerically, so the in-memory store put '9' after '10' where a database puts it before — and compared '1e2' and '100' equal, which made the keyset filter treat one of two distinct records as already passed and drop it, while the page still reported itself finished. Entity ids are client input, so this was reachable on purpose. Identifiers are compared by bytes now.
putReceipt() reported every database error as "mutation identity already recorded." A deadlock, a dropped connection and a missing table all arrived as "already processed" — and a caller that trusts that answer drops the write and reports success.
Breaking: Store::prune() is on the contract. The commit log is the only thing here that grows without bound, and a host given the contract had no way to reach the pruning both adapters already implemented.
Breaking: beforeCommit() takes the space on both adapters. They differed before, so the fault-injecting store could only extend the in-memory one — and the rollback test had never run against a database on any driver. It now runs on all of them.
Breaking: commitDraft() without a draft, and OutboxStore::append() with a duplicate identity, are refused the same way on both adapters instead of being silent on one and a raw PDOException on the other.
Performance
A selective bootstrap page costs the page, not the space. The field index was unreachable: the query drove from the record scan and probed sync_fields once per record. Measured on a 32,000-record space: 331ms → 0.2ms.
The outbox is indexed. On a 5,000-deep backlog: queueing 4,955ms → 583ms, draining ~32,000ms → 957ms. That cost lands on the device.
FrozenBootstrapSessions no longer grows without bound.
Tests
The parity suite pages a hostile identifier set through both adapters one record at a time. It used a friendly alphabet before, which is why the ordering defect could hide in it.