Cropwright — Review Assign + Undo

Cropwright — Review Assign + Undo A sequence diagram generated by Archify. class hotkey / Enter assign(item, classId) drop item, advance cursor putCropLabel(id, classId) 200 OK recordWrites([id]) (failure) rejects restore + rollback toast Z — undoLast() undoLast() POST undo_batch restored crop(s) re-insert, restore cursor Optimistic assign Rollback on failure Z undo Operator · keyboard · Sequence participant Operator keyboard review page · queue state · Sequence participant review page queue state controller · assign / undo · Sequence participant controller assign / undo undoStore · Z ring buffer · Sequence participant undoStore Z ring buffer api.ts · wire calls · Sequence participant api.ts wire calls toastStore · rollback · Sequence participant toastStore rollback Legend request return security default message

Removal happens before the network call resolves

  • • removeFromQueue() drops the item and advances the cursor synchronously, returning a restore closure
  • • Every caller (assign, discard) must invoke that closure on failure — mutation testing caught exactly this bug three times before extraction

One undo, however many crops it touched

  • • undoStore is a Z ring buffer shared by bulk label, move, discard, VLM-accept-all, and a dismiss — one press reverts the whole action, in the order it actually happened
  • • Discard deliberately does NOT push an undoStore entry; it's reversible via a separate Dismissed panel, not Z

Undo restores server state first, then the local queue

  • • POST /crops/label/undo_batch is the source of truth for what got restored — the controller never guesses the pre-undo state
  • • The cached reason (ReviewItem carries a served reason Crop doesn't) is reattached from a local map at removal time, not invented