Source: initial code review, 2026-10-06 · Priority: P1 · Related: #4
Problems
- The shared toolkit queue is bypassed. When no transport is supplied,
widget-ids.ts:349-351 creates its own new ConnectionService. That instance does not share the main queue, busy flag or login guard, so a reservation can run toolkit commands at the same time as a publish or login.
- A receipt can get stuck in
preparing. If the app crashes after the preparing receipt is committed but before any batch starts (:315-348), inspect reports "may have completed; retry disabled" and reserve throws "already recorded". The key is tied to the revision, so the user has to make an unrelated edit to continue.
- Reservation isn't serialized with draft saves. A save during allocation (
services.ts:317-341) changes the revision, the receipt becomes uncertain, and the allocated IDs are wasted.
- Landing reservation can lose track of allocated IDs. If
target() throws after reserve completes, the IDs are allocated but the preparation record is not updated (landing.ts:341-361).
- Cross-receipt scans can fail on bad data.
JSON.parse in the scan (widget-ids.ts:411-421) and in landing.ts:227 isn't guarded.
Fix
- Pass in
services.connection.
- Treat
preparing with no recorded batches as retryable blocked.
- Block
assertEditable while a reservation is running, or run the reservation inside the draft queue.
- Save the receipt before calling
target().
- Guard the parses.
Acceptance
- Tests cover each scenario.
Source: initial code review, 2026-10-06 · Priority: P1 · Related: #4
Problems
widget-ids.ts:349-351creates its ownnew ConnectionService. That instance does not share the main queue, busy flag or login guard, so a reservation can run toolkit commands at the same time as a publish or login.preparing. If the app crashes after thepreparingreceipt is committed but before any batch starts (:315-348),inspectreports "may have completed; retry disabled" andreservethrows "already recorded". The key is tied to the revision, so the user has to make an unrelated edit to continue.services.ts:317-341) changes the revision, the receipt becomesuncertain, and the allocated IDs are wasted.target()throws afterreservecompletes, the IDs are allocated but the preparation record is not updated (landing.ts:341-361).JSON.parsein the scan (widget-ids.ts:411-421) and inlanding.ts:227isn't guarded.Fix
services.connection.preparingwith no recorded batches as retryableblocked.assertEditablewhile a reservation is running, or run the reservation inside the draft queue.target().Acceptance