- wrap writePixels in state.storage.transactionSync so a partial multi-chunk
failure doesn't leave the canvas half-written or out of sync with the WS
broadcast
- size new chunk buffer against chunkSize(chunkId) instead of the persisted
blob's length so writes after a canvas-grow no longer silently drop OOB
bytes in the formerly-last short chunk
- refund the cooldown row when writePixels throws so transient storage
errors stop soft-DOSing the user (and halving image-uploader throughput)
- bound readAllChunks by chunk_id < CHUNK_COUNT and trim oversized blobs so
orphan rows from a future shrink no longer crash GET /api/canvas
- require a positive Content-Length on /api/place (411) and reject above the
pre-parse cap (413); previously a missing or zero header bypassed the cap
- drop String(err) from the 500 response body
- drain the INSERT cursor symmetrically with the UPDATE branch in tryAcquire
- assert CHUNK_BYTES <= 2 MB at module load (DO SQLite per-cell BLOB cap)
- correct the inverted webSocketClose comment and guard the re-close call
- add tests for missing / zero / oversized Content-Length
Plan: plans/260510-0232-fix-do-migration-followups/phase-01-do-storage-atomicity.md