feat: send messages while sync --follow is running (IPC delegation)#92
Open
najibninaba wants to merge 9 commits intosteipete:mainfrom
Open
feat: send messages while sync --follow is running (IPC delegation)#92najibninaba wants to merge 9 commits intosteipete:mainfrom
najibninaba wants to merge 9 commits intosteipete:mainfrom
Conversation
- Add lock.ErrLocked sentinel and ContentionError typed error - Acquire() returns *ContentionError wrapping ErrLocked on flock contention - Non-contention flock errors return distinct 'flock lock file' error - Add resolveStoreDir() helper in root.go, used by newApp and doctor - Doctor only reports LockHeld=true on actual contention (errors.Is) - Non-contention lock probe errors in doctor are surfaced, not mislabeled - Tests verify errors.Is(err, ErrLocked) and errors.As(*ContentionError) Task 1 of IPC send delegation plan.
- Add SendTextParams, SendFileParams, SendFileMetadata, SendResult types - Implement App.SendText() and App.SendFile() as canonical send entry points - Move MIME detection, upload, proto construction, DB persistence to app layer - Consolidate chatKindFromJID (send_file.go) with chatKind (sync.go) - Extend fakeWA with send/upload recording and configurable responses - CLI commands now only do flag parsing + output formatting - Delete cmd/wacli/send_file.go (all logic moved to internal/app) - Tests cover text send, file classification, and best-effort persistence - Best-effort DB persistence: post-send DB errors don't fail the command Task 2 of IPC send delegation plan.
- Add SendSocketPath(storeDir) for canonical socket location - Add ErrSendDelegateUnavailable sentinel for transport failures - Add SendDelegateRemoteError for server-side application errors - Add SendDelegateProtocolError for version mismatch - Add DelegateSendText/DelegateSendFile client functions - Add sendDelegateServer with sequential accept loop - JSON-over-Unix-socket, one req/resp per connection - Stale socket cleanup on server start (only removes actual sockets) - Socket file removed on server close and serve loop exit - Timeout propagation via TimeoutMS derived from context deadline - Tests cover: round trips, missing/refused socket, remote errors, protocol version mismatch, stale cleanup, close cleanup, timeout Task 3 of IPC send delegation plan.
- Start send delegate server in App.Sync for SyncModeFollow only - Server starts after event handler registration, before Connect - Server shutdown deferred on all sync exit paths - Socket startup failure in follow mode is fatal (prevents partial state) - SyncModeOnce and SyncModeBootstrap do not host the socket - Add connectMu sync.Mutex to wa.Client for connect serialization - Connect() serialized end-to-end with connectMu.Lock/defer Unlock - ReconnectWithBackoff inherits serialization via Connect - Integration test: delegated send succeeds during follow-mode sync - Integration test: once mode does not create socket - AST structural test verifies connectMu lock discipline in Connect - All tests pass with -race flag Task 4 of IPC send delegation plan.
- On newApp lock contention (ErrLocked), attempt IPC delegation - If IPC unavailable (no socket), return original lock error unchanged - If IPC returns remote error, surface that error directly - If IPC succeeds, format output identically to direct send - Non-lock newApp failures return unchanged (no delegation attempt) - Normalize --file to absolute path before delegation or direct send - JSON/human output format is identical for direct and delegated sends Fallback chain: ErrLocked → try socket → ErrUnavailable → lock error Task 5 of IPC send delegation plan.
- Add 0.3.0 release notes for automatic IPC delegation - Add 'Send while syncing' section with usage examples - Add 'Send delegation (IPC)' section to spec with fallback chain - Document .send.sock in storage layout - Describe protocol versioning, serialization, and cleanup Task 6 of IPC send delegation plan.
…t shutdown - Add 1s sendDelegateReadTimeout so stalled clients cannot block future sends - Track activeConn in sendDelegateServer with activeConnMu guard - handleConn registers/deregisters active conn and sets read deadline before Decode - Close() force-closes active connection before waiting for serve() to exit - Add TestSendDelegateServer_CloseReturnsPromptlyWithStalledClient regression test - Existing IPC tests and sync tests continue to pass with -race Fixes review MF1 (head-of-line blocking) and S3 (missing stalled client test).
…ions - delegateSend rejects send_file responses where Result.File is nil with SendDelegateProtocolError (prevents nil-pointer panic in CLI) - startSendDelegateServer chmod's socket to 0600 after ListenUnix - On chmod failure, listener is closed and socket removed before error - Add TestDelegateSendFile_RejectsNilFileMetadata regression test - Add TestSendDelegateServer_SocketPermissions regression test Fixes review S1 (response shape validation) and S2 (socket permissions).
- Fire 3 concurrent DelegateSendText calls during follow-mode sync - Verify all 3 succeed and all 3 reach the WA send call - Validates server serialization handles queued concurrent clients - Passes with -race flag Fixes review S4 (missing concurrent delegation test).
This was referenced Mar 22, 2026
|
Tested on macOS with a locally built wacli 0.5.0. Build: Repro before this change:
Result with current build:
Notes:
|
|
I have tested it on Arch and this patch is working. @steipete, please merge it into main. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Fixes #70.
wacli send textandwacli send filenow work whilesync --followis running. When the store lock is held, the send command automatically delegates to the running sync process via a Unix-domain socket (<store>/.send.sock). This is fully transparent — output is identical to a direct send.Design
The fallback chain in each send command:
ErrLocked→ attempt IPC delegation via.send.socksync --once) → return the original lock errorOnly
sync --followhosts the IPC server.sync --onceandauthdo not.Architecture
internal/lock):ErrLockedwitherrors.Issupport for clean branchinginternal/app/send.go):App.SendText/SendFileextracted from CLI layerinternal/app/send_ipc.go): JSON-over-UDS protocol (version 1), client + serverinternal/app/sync.go): follow-mode starts/stops the IPC servercmd/wacli/send.go,send_file_cmd.go): lock → IPC → original errorconnectMuserializesConnect; IPC server serializes delegated sends; file paths normalized to absolute before delegationRobustness
Close()ensures prompt shutdown0600File != nil(prevents nil-pointer on malformed peers)Commits
2d8e812ErrLockedsentinel + sharedresolveStoreDir48e0dc3App.SendText/SendFileintointernal/app/send.go056a78cf6e7a57connectMuserialization7ad832a1958319cfa8c7c8c3f53506007a559f3Test coverage
lock_test.go)send_test.go)send_ipc_test.go)send_ipc_test.go)send_ipc_test.go)0600(send_ipc_test.go)sync_test.go)sync_test.go)sync_test.go)connectMulock discipline inConnect(locking_regression_test.go)All tests pass with
-race.Breaking changes
None. Existing behavior is unchanged when the lock is free.