Skip to content

fix(mobile): preserve drafts after storage read failures - #213

Merged
leoisadev1 merged 8 commits into
mainfrom
fix/mobile-outbox-read-failure
Sep 13, 2026
Merged

leoisadev1 merged 8 commits into
mainfrom
fix/mobile-outbox-read-failure

Conversation

@leoisadev1

@leoisadev1 leoisadev1 commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Problem

A failed mobile draft read could look like an empty store. The next save could overwrite saved work. An unreadable outbox record previously rejected the whole queue, so valid queued messages never reached delivery after restart. A send that failed in flight after reconnect was treated as a deterministic rejection because SocketReadError only says "An error occurred during Read". The drain then restored the queued text into a draft and deleted the outbox entry.

Changes

Draft saves now require successful hydration. Final flush retries pending edits after a successful read.

Outbox load returns readable queued messages and leaves unreadable files on disk with diagnostics. Hydration publishes those readable messages so they can send after reconnect. Environment cleanup still stops if any outbox record cannot be read, so unread attachment owners are not deleted.

Queued delivery classifies failures by error tag first:

  • Restore (payload is bad): OrchestrationDispatchCommandError, EnvironmentAuthorizationError
  • Retry (transport): ConnectionTransientError, RpcClientError (any socket reason or protocol defect), EnvironmentRpcUnavailableError, EnvironmentNotRegisteredError

Untagged errors still fall back to the message matcher.

Adaptation

Reviewed ports of T3 Code #9710 and T3 Code #10245. Akeru has no hosted cloud draft archive or file-backed attachment cleanup yet, so this keeps local draft fail-closed behavior and tagged retry classification. Outbox hydration recovers readable records instead of blocking the whole queue on one unread file.

Scope

Mobile draft persistence, thread outbox load/cleanup, and send-failure classification. No web UI, native modules, providers, or contracts.

Verification

  • vp test run apps/mobile/src/state/use-composer-drafts.test.ts apps/mobile/src/state/thread-outbox.test.ts — 45 passed (28 outbox), including mixed valid/corrupt files through expo storage plus the drain/pending-task atom, cleanup that still stops on unread records, draft read/decode preservation, flush retry, and tagged transport retries
  • vp lint on the touched files — 0 warnings, 0 errors
  • vp run --filter @t3tools/mobile typecheck — passed

This is persistence and classification logic. No rendered web or desktop UI change. Native Android/iOS device runs are blocked on this Linux host: no adb, Android SDK, or Xcode. Unit tests use temporary-file mocks and cover the real-source failure cases.

Limitations

  • Unreadable outbox files stay on disk until they can be decoded. Cleanup will not remove an environment while any of those files remain unreadable.
  • Multiple new-task drafts, queued timeline rows, composer uploads, and native dictation are separate PRs.

Created with Grok 4.6 High in Grok Build via Orca.

A failed mobile draft or outbox read looked like an empty store. The next
save could overwrite saved work, and environment cleanup could drop queued
messages it could not list.

Draft saves now require successful hydration. Outbox loads no longer return
a partial queue. Removing an environment stops if its outbox cannot be read.
In-flight send failures retry by error tag, including RpcClientError socket
reads whose message is only "An error occurred during Read".

Adapted from T3 Code pingdotgg#9710 and pingdotgg#10245. Akeru has no hosted cloud draft
archive, so this keeps the local fail-closed behavior only.

Created with Grok 4.6 High in Grok Build via Orca.
@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
akeru-bot-landing Building Building Sep 10, 2026 5:37pm UTC

Request Review

@github-actions github-actions Bot added size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Sep 10, 2026
@greptile-apps

greptile-apps Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR strengthens mobile draft and outbox persistence around incomplete storage reads and in-flight writes. Readable queued messages can hydrate while incomplete records remain visible for safe cleanup, connected sessions retry incomplete hydration for a bounded window, and final draft flushes include edits queued during earlier persistence work.

Confidence Score: 5/5

Safe to merge; there are no outstanding blocking issues.

No new findings remain. The corrupt-record hydration concern was withdrawn after greptile-apps[bot] accepted the documented fail-closed cleanup rationale. PRRT_kwDOUFhSmc6hMaI0 was manually resolved by leoisadev1 without explanation. PRRT_kwDOUFhSmc6hMx_W was manually resolved by leoisadev1 without explanation. PRRT_kwDOUFhSmc6h8H9o was manually resolved by leoisadev1 without explanation. PRRT_kwDOUFhSmc6h8H9v was manually resolved by leoisadev1 without explanation.

Reviews (6): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread apps/mobile/src/state/thread-outbox-storage.ts Outdated
A mixed outbox with one unreadable record rejected the whole load, so valid
queued messages never reached delivery after restart. Load now returns the
readable messages and keeps unread files on disk with diagnostics.

Environment cleanup still stops when any record cannot be read, so attachment
owners from unread files are not deleted.

Created with Grok 4.6 High in Grok Build via Orca.
A valid pending-task file next to a corrupt sibling hydrates into the same
queue atom the drain and pending-task list read. The unread file stays on
disk, and environment cleanup still refuses to delete unknown attachment
owners.

Created with Grok 4.6 High in Grok Build via Orca.
@leoisadev1

Copy link
Copy Markdown
Member Author

This is Leo's agent.

The earlier reply on dd64656e treated fail-closed load as the whole product answer. That was wrong for delivery. Head acf39be04 separates the two paths:

  • Hydration/delivery: storage.load() no longer throws on a bad sibling. Readable queued messages are published onto queuedMessagesByThreadKeyAtom, which is what useThreadOutboxMessages, the outbox drain, composer queue count, and usePendingNewTasks read.
  • Cleanup/attachments: clearEnvironment still throws when unreadRecords is non-empty. Unreadable files are left on disk with ThreadOutboxStorageError diagnostics. Nothing deletes or skips them.

Evidence from vp test run apps/mobile/src/state/thread-outbox.test.ts (28 passed): mixed read/json/schema failures return the valid message-1 and keep message-2.json; manager load publishes that message; a pending-task file next to { is visible to drain (resolveThreadOutboxDeliveryAction is send) while cleanup still refuses and both files remain.

Remaining limitation: a permanently corrupt file has no in-app discard UI. It stays on disk and blocks environment removal until it can be decoded. Readable queued work still sends.

Comment thread apps/mobile/src/state/thread-outbox-manager.ts
A mixed load published readable messages, then cached that result. A sibling
file that became readable later never joined the drain queue until JS
restarted. Incomplete loads now drop the cache, and drain asks for another
read on reconnect.

Created with Grok 4.6 High in Grok Build via Orca.
Comment thread apps/mobile/src/state/use-thread-outbox-drain.ts Outdated
Comment thread apps/mobile/src/state/use-composer-drafts.ts Outdated
Comment thread apps/mobile/src/state/use-thread-outbox-drain.ts Outdated
@leoisadev1
leoisadev1 merged commit 5b59149 into main Sep 13, 2026
11 checks passed
@leoisadev1
leoisadev1 deleted the fix/mobile-outbox-read-failure branch September 13, 2026 21:41
This was referenced Sep 13, 2026

This branch was previously deployed

1 inactive deployment
Preview — 49e5d1d7 Deployed Sep 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant