src/sync/) provides bidirectional synchronization between profClaw’s internal ticket store and external platforms: GitHub Issues, Jira, and Linear.
Core Types
SyncAdapter Interface
Each platform implementsSyncAdapter:
Sync Engine Config
Conflict Resolution
When the same ticket is modified locally and remotely between sync cycles, theConflictStrategy determines which value wins:
| Strategy | Behavior |
|---|---|
local_wins | profClaw value always overwrites remote |
remote_wins | Remote value always overwrites profClaw |
latest_wins | Whichever has the most recent updatedAt wins |
manual | Conflict stored for user resolution |
SyncConflict record is created for every conflict:
Sync Queue
Operations are queued and processed with retry:Webhook-Driven Sync
When webhooks are enabled (enableWebhooks: true), inbound webhook events from GitHub/Jira/Linear trigger immediate sync operations instead of waiting for the polling interval:
- Webhook arrives at
/api/webhooks/:platform - Adapter calls
parseWebhook(payload)to extract aWebhookEvent - Sync engine processes the event immediately
- Local ticket state updated within seconds of the external change
Pull Sync
On each polling cycle, the engine callsadapter.listTickets({ updatedAfter: lastSyncAt }) with the cursor from the previous sync. This fetches only changed tickets, minimizing API quota usage.
Push Sync
When a local ticket is created or updated, aSyncOperation is added to the queue. The adapter’s createTicket or updateTicket method is called asynchronously.
Status and Priority Mapping
Each adapter implements bidirectional mapping functions. For example, the GitHub adapter maps:stateType (triage, backlog, started, completed, cancelled) to profClaw’s TicketStatus enum.
Multi-Device Sync
The sync system also handles profClaw-to-profClaw synchronization for multi-instance setups. Two profClaw instances can sync their task and conversation state over Tailscale or direct HTTP, using the sameSyncAdapter interface with a profclaw platform adapter.