CopilotKit / CopilotKit/outpost

Sync engine: echo detection can never fire, and inbound changes are silently discarded

Open
#139 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: sync roadmap roadmap: next
Dominant language
TypeScript
Stars
7
Forks
3
Avg merge
7d 16h
Merged PRs (30d)
15

Description

Summary

Echo detection in the bidirectional sync engine appears to be implemented but can never fire,
so a change pushed out to a tracker can be re-ingested as a new inbound change. Found during
a code review of an unrelated PR.

Findings — packages/outpost/shared/src/sync/engine.ts

1. Echo detection queries the wrong direction. Outbound pushes are recorded with
source='outpost', but shouldSync queries the reverse plugin pair, so the record it needs
is never the record it looks for. The guard is structurally unreachable rather than merely
buggy.

2. targetPlugin inside payloadHash makes cross-direction matching impossible.
Because the hash includes the target, the same logical change hashes differently in each
direction — so even a corrected query could not match.

3. The echo check runs at enqueue but the event is written at execute. Two changes
arriving inside that window both pass the check, producing duplicate TRACKER_SYNC jobs.

4. Inbound webhook changes are silently discarded while reporting success. Comment, label,
priority, assignee and close changes arriving inbound are dropped, and SyncResult still
reports success — so a sync that did nothing looks like a sync that worked.

5. computeHash's array replacer strips nested keys, so distinct changes can collide on
the same hash.

Why it matters

Sync is currently exercised lightly, which is why this has not produced a visible incident.
As soon as bidirectional Linear sync carries real traffic, the failure mode is an echo loop
between Outpost and the tracker, or silently dropped inbound edits — both hard to diagnose
from the outside because the code reports success.

Acceptance criteria

  • shouldSync matches the direction that pushes actually record, with a test that fails
    against the current implementation
  • payloadHash is direction-independent
  • The echo record is written atomically with (or before) the enqueue, so two rapid changes
    cannot both pass
  • Inbound comment/label/priority/assignee/close changes are either applied or reported as
    unsupported — never silently dropped with success: true
  • computeHash covers nested keys

Related: #95, #111

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/outpost/shared/src/sync/engine.ts, reading shouldSync, payloadHash, computeHash, enqueue/execute handling, and SyncResult. Trace how outbound records and inbound webhook changes move through these entry points, then add focused tests for direction matching, atomic echo handling, nested-key hashing, and unsupported inbound changes. Done means every acceptance criterion is covered without silent success.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend, distributed-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.