Skip to content

fix: LXMF pending-row orphans and Windows esbuild main build - #800

Merged
rinchen merged 3 commits into
mainfrom
runr
Aug 5, 2026
Merged

fix: LXMF pending-row orphans and Windows esbuild main build#800
rinchen merged 3 commits into
mainfrom
runr

Conversation

@rinchen

@rinchen rinchen commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

  • Delete optimistic reticulum-pending-* SQLite rows when LXMF send rekeys to a real message hash, including while delivery is still sending (also cleans twins in db:saveReticulumMessage). Seen in Runr’s AIBot developer bundles as duplicate stuck-Sending rows after hydrate.
  • Fix Windows dist:win packaging: #798 spawned esbuild/bin/esbuild directly, but on win32 postinstall leaves that path as a Node shim so build:main:prod exits 1 with no output. Switch esbuild-main-build.mjs to the esbuild JS API so MESH_CLIENT_BUILD_INFO still embeds correctly.
  • Peer Details React fix: harden MeshCore BLE timeout/disconnect resilience #185 is already fixed post-5.26.0 (feat(rrc): per-peer DMs, peer-detail #185 fix, and TCP reconnect unstick #795). Missing bot replies in those bundles remain primarily LAN-only hub / PN sync reachability, not the pending-row bug.

Test plan

  • reticulumOutboundRetry.test.ts — pending→hash deletes; empty ids skipped
  • reticulum-db-handlers.test.ts — pending twin dropped while still sending
  • esbuild-main-build.test.mjs — JS API contract (no spawnSync of bin shim)
  • Local MESH_CLIENT_BUILD_INFO=… pnpm run build:main:prod embeds channel stamp
  • CI: Build Binaries Windows dist:win green after merge
  • Manual: send LXMF; one DB row while Sending; no duplicate pending after mid-send restart

Stuck Direct Completes left reticulum-pending-* twins beside the real
hash after hydrate; delete optimistic rows on rekey and on save.
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rinchen, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 11ec177f-227c-4fa1-bad2-f70ae76f4efc

📥 Commits

Reviewing files that changed from the base of the PR and between 2a45674 and 0513283.

📒 Files selected for processing (10)
  • scripts/esbuild-main-build.mjs
  • scripts/esbuild-main-build.test.mjs
  • src/main/ipc/reticulum-db-handlers.test.ts
  • src/main/ipc/reticulum-db-handlers.ts
  • src/preload/index.ts
  • src/renderer/lib/ingest/reticulumIngest.ts
  • src/renderer/lib/reticulum/reticulumIngestMerge.ts
  • src/renderer/runtime/useReticulumRuntime.ts
  • src/shared/buildInfo.ts
  • src/shared/electron-api.types.ts
📝 Walkthrough

Walkthrough

The change removes optimistic Reticulum pending records when a real message hash is available. Cleanup now also applies while delivery status is sending. Tests cover rekeying, duplicate hashes, empty identifiers, and database persistence.

Changes

Reticulum message cleanup

Layer / File(s) Summary
Outbound hash cleanup
src/renderer/lib/reticulum/reticulumOutboundRetry.ts, src/renderer/lib/reticulum/reticulumOutboundRetry.test.ts
shouldDeletePriorReticulumOutboundHash now deletes distinct, non-empty prior identifiers, including reticulum-pending-* identifiers. Tests cover matching and empty identifiers.
Database twin cleanup
src/main/ipc/reticulum-db-handlers.ts, src/main/ipc/reticulum-db-handlers.test.ts
Saving a real message removes a matching pending twin even when the real message has sending status. Integration coverage verifies that only the normal message remains.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title accurately names LXMF pending-row orphans but also claims a Windows esbuild main-build change that is not present in the changeset. Remove the unsupported Windows esbuild clause and keep the title focused on LXMF pending-row orphan cleanup.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch runr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/ipc/reticulum-db-handlers.ts (2)

177-184: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make pending-row cleanup and message persistence atomic.

Lines 178-183 commit the pending-row deletion before the later lookup, update, or insert. If a later database operation fails, the catch path leaves the pending row deleted without saving its replacement.

Wrap the cleanup and the remaining save path in one SQLite transaction. Add a failure-path test that verifies the pending row remains when the replacement write fails.

As per path instructions, preserve SQLite integrity during hydration and rekeying, including failure handling where relevant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/ipc/reticulum-db-handlers.ts` around lines 177 - 184, Make the
pending-row deletion and the subsequent message lookup, update, or insert in the
handler containing the reticulum message save flow execute within one SQLite
transaction, so any replacement-write failure rolls back the cleanup. Preserve
existing hydration and rekeying integrity and error handling, and add a
failure-path test that confirms the pending row remains when the replacement
write fails.

Source: Path instructions


177-183: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Delete only the pending row that the real hash replaces.

Line 177 deletes every pending row with the same identity, sender, payload, and a timestamp within 60 seconds. Two distinct sends can satisfy these fields. Finalizing one send can then delete the other send's persisted sending state. A restart before the second send completes cannot hydrate or retry it.

Pass an explicit prior pending ID through the save contract. Delete only that exact row. Add a regression test with two identical outbound payloads in the same time window.

As per path instructions, preserve SQLite integrity during hydration and rekeying.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/ipc/reticulum-db-handlers.ts` around lines 177 - 183, Update the
save/finalization contract around the message persistence handler to carry the
exact prior pending message ID, and use that ID in the DELETE query instead of
matching identity, sender, payload, and timestamp. Ensure rekeying removes only
the pending row replaced by the real hash, preserves SQLite integrity during
hydration and rekeying, and add a regression test covering two identical
outbound payloads within the same time window.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/main/ipc/reticulum-db-handlers.ts`:
- Around line 177-184: Make the pending-row deletion and the subsequent message
lookup, update, or insert in the handler containing the reticulum message save
flow execute within one SQLite transaction, so any replacement-write failure
rolls back the cleanup. Preserve existing hydration and rekeying integrity and
error handling, and add a failure-path test that confirms the pending row
remains when the replacement write fails.
- Around line 177-183: Update the save/finalization contract around the message
persistence handler to carry the exact prior pending message ID, and use that ID
in the DELETE query instead of matching identity, sender, payload, and
timestamp. Ensure rekeying removes only the pending row replaced by the real
hash, preserves SQLite integrity during hydration and rekeying, and add a
regression test covering two identical outbound payloads within the same time
window.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 85f718dd-e92f-48d7-96f0-8c28763aef9d

📥 Commits

Reviewing files that changed from the base of the PR and between 6547309 and 2a45674.

📒 Files selected for processing (4)
  • src/main/ipc/reticulum-db-handlers.test.ts
  • src/main/ipc/reticulum-db-handlers.ts
  • src/renderer/lib/reticulum/reticulumOutboundRetry.test.ts
  • src/renderer/lib/reticulum/reticulumOutboundRetry.ts

Direct spawn of esbuild/bin/esbuild fails on win32 where postinstall
leaves a Node shim; use esbuild.build so dist:win embeds build info.
@rinchen rinchen changed the title fix(reticulum): drop orphan LXMF pending rows while still sending fix: LXMF pending-row orphans and Windows esbuild main build Aug 5, 2026
Carry replaces_message_hash into saveReticulumMessage and delete+upsert
in one transaction so twin payloads stay intact and insert failures
roll back pending cleanup.
@rinchen
rinchen merged commit ac2f364 into main Aug 5, 2026
12 checks passed
@rinchen
rinchen deleted the runr branch August 5, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant