#191
feat(drafts): show when a draft has not reached the environment
What
A composer draft now says Not synced while its latest edit exists only on this device. Before, edits made while the environment was unreachable were kept and saved on reconnect, but nothing told you they hadn't been shared yet.
- Status per draft.
selectDraftSyncStatusreturns one of:syncedsaving(waiting out the 1 s pause in typing, or on the wire)- one of four stuck reasons:
offline,unsupported(reached but keeps no drafts),too_large,failed
- The mark sticks until the save lands. A save that can't go records why on the edit (
DraftEdit.stalled). The mark carries through later typing and is cleared only when a write of that draft is answered, so a reconnect doesn't clear the chip before the environment has the text. - Composer chip. A muted cloud-slash "Not synced" chip sits beside the send button, with a tooltip giving the reason. It is hidden for
syncedandsaving, so ordinary typing never flickers it.ComposerDraftStore.getSyncStatusexposes the status to the composer. - Failed saves are retried. A save that fails on a live connection is now tried again with backoff (1 s up to 30 s) instead of waiting for the next keystroke or reconnect. A save cut off by a dropped connection waits for the reconnect.
- Flush order. A reconnect saves waiting drafts least recently edited first.
Edits and new drafts made offline already persisted in the cached client state, and the existing revision/tombstone rules still decide what survives a conflict. An edit is only dropped when the environment refuses it, for one of two reasons: a send or discard that happened elsewhere, or a deleted session.
Sidebar draft cards don't exist yet. When they land, they should read selectDraftSyncStatus the same way (noted in docs/decisions/composer-drafts.md).
Tests
environment-client/tests/drafts-offline.test.tsuses one fake environment shared by two clients. Client A disconnects, edits a draft and starts another, then reconnects. Both drafts appear on client B in order, and A's chip stays "not synced" until the saves are answered.- Also covered: retry after a failed save; a connection dropped mid-save; a draft too large to sync; a listing failure; a draft being sent not counted as unsynced; an offline clear of a never-saved draft; an in-flight save not written twice; an environment that keeps no drafts.
app-core/tests/environment-drafts.test.tsxchecks that the chip appears offline and clears after reconnect.- environment-client: full suite and typecheck pass. app-core: typecheck passes; the full suite passes except the known-flaky
boundary.test.tsESLint timeout under load.
🤖 Generated with Claude Code
Activity
14greptile-apps[bot]reviewed28d ago
greptile-apps[bot]reviewed28d ago
greptile-apps[bot]reviewed28d ago
greptile-apps[bot]reviewed28d ago
calledforthreviewed28d ago
calledforthreviewed28d ago
calledforthreviewed28d ago
calledforthreviewed28d ago
calledforthreviewed28d ago
calledforthreviewed28d ago
[Medium risk] Adds draft sync status display to the composer.
The PR appears safe to merge; no new actionable issue or outstanding previous finding remains.
Summary
The PR adds per-draft sync status and a composer “Not synced” indicator, retries failed saves, and flushes waiting drafts in edit order. Since the previous review, it preserves the unsynced mark on text typed during an in-flight save, resets backoff after a successful write, and adds coverage for the in-flight case.
Diagram
Reviews (2) · Last reviewed commit: "fix(drafts): keep the unsynced mark unti..."