Skip to content

feat: bidirectional comment sync and attachment pull for Linear#2824

Open
aphexcx wants to merge 6 commits intosteveyegge:mainfrom
aphexcx:main
Open

feat: bidirectional comment sync and attachment pull for Linear#2824
aphexcx wants to merge 6 commits intosteveyegge:mainfrom
aphexcx:main

Conversation

@aphexcx
Copy link
Copy Markdown

@aphexcx aphexcx commented Mar 25, 2026

Summary

  • Adds bidirectional comment synchronization for the Linear integration — comments created in beads appear in Linear and vice versa
  • Adds attachment metadata pull from Linear → beads (URLs, titles, creators stored locally; pull-only, no file upload)
  • Uses optional interfaces (CommentSyncer, AttachmentFetcher) so Jira/ADO/GitHub/GitLab adapters are unaffected
  • Deduplication via external_ref on comments and attachments prevents duplicates across syncs
  • Incremental sync with per-issue awareness — newly synced issues get full comment history, previously synced issues only fetch changes since last sync

Schema changes

  • comments / wisp_comments: adds external_ref VARCHAR(255) and updated_at DATETIME columns
  • New attachments table (id, issue_id, external_ref, filename, url, mime_type, size_bytes, source, creator, created_at)
  • Go migration (013_comment_sync_columns) for server-mode Dolt; embedded SQL migrations (0024, 0025) for embedded mode

New CLI flags

  • --no-comments — disable comment sync
  • --no-attachments — disable attachment pull
  • --comments-only — sync only comments, skip issues and attachments

Architecture

  • tracker.CommentSyncer interface: FetchComments(ctx, externalIssueID, since) + CreateComment(ctx, externalIssueID, body)
  • tracker.AttachmentFetcher interface: FetchAttachments(ctx, externalIssueID)
  • storage.CommentRefStore interface: comment CRUD with external_ref tracking
  • storage.AttachmentStore interface: attachment CRUD
  • Both DoltStore and EmbeddedDoltStore implement the new storage interfaces
  • Sync engine caches external issue lookups to avoid N+1 API calls

Test plan

  • bd linear sync --dry-run shows comments and attachments to sync
  • bd linear sync pulls comments from Linear, pushes local comments to Linear
  • Re-running sync does not duplicate comments (external_ref dedup)
  • --no-comments skips comment sync
  • --comments-only syncs only comments, skips issues and attachments
  • Inline images in descriptions preserved via issue sync
  • Schema migration on existing Dolt server databases
  • EmbeddedDolt mode comment/attachment operations
  • Jira/ADO sync unaffected (no interface changes required)

🤖 Generated with Claude Code

aphexcx and others added 3 commits March 24, 2026 23:26
…bd-1ob)

Implements Linear comment and attachment synchronization:

- Schema: add external_ref/updated_at to comments table, create attachments table
- Types: extend Comment with ExternalRef/UpdatedAt, add Attachment struct
- Tracker framework: add optional CommentSyncer and AttachmentFetcher interfaces
- Linear client: add GraphQL queries for issue comments and attachments
- Linear tracker: implement CommentSyncer and AttachmentFetcher
- Sync engine: add comment sync (pull+push) and attachment pull phases
- Storage: add CommentRefStore and AttachmentStore optional interfaces
- CLI: add --no-comments, --no-attachments, --comments-only flags

Design: optional interfaces ensure non-Linear adapters need no changes.
Comments use external_ref matching to avoid duplicates. Attachments are
metadata-only (URL, filename, size) — no file downloads.
- P1: Add Go migration (013_comment_sync_columns) for server-mode Dolt
  to ALTER TABLE comments/wisp_comments adding external_ref + updated_at
  columns and CREATE TABLE attachments
- P2: Skip attachment pull when --comments-only is set
- P2: Use per-issue cutoff for comment sync (zero time for issues that
  have never been synced, global cutoff only for previously-synced issues)
- P2: Add CommentRefStore and AttachmentStore implementations to
  EmbeddedDoltStore (comment_ref.go, attachments.go)
- MAJOR: Eliminate N+1 FetchIssue calls by building a shared ext issue
  cache once, used by both comment sync and attachment pull phases
- MINOR: Fix variable shadowing (t -> ts) in linear/tracker.go where
  time.Parse shadowed the *Tracker receiver
- MINOR: Remove unused Metadata struct from linear Attachment type
  (GraphQL query never requests it, so MimeType was always empty)
- MINOR: Update last_comment_sync and last_attachment_sync timestamps
  unconditionally after successful sync pass, not just when items synced

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@maphew
Copy link
Copy Markdown
Collaborator

maphew commented Mar 27, 2026

Heads up @aphexcx — we just merged #2862 which also registers migration 013 (dropping the child_counters FK). You'll need to renumber your migration to 014 and rebase to pick up the change.

We went with #2862 first because it was a smaller, self-contained schema fix that unblocked set-state for agent beads — merging it quickly reduced the blast radius for other PRs that depend on that path working.

Your bidirectional comment sync work looks solid — the optional interface design is well thought out. Looking forward to seeing this land once the migration numbering is sorted and the remaining test plan items are checked off. Thanks for the contribution!

aphexcx and others added 3 commits March 28, 2026 23:36
…(bd-1ob)

P1: last_comment_sync / last_attachment_sync no longer advance on push-only
or partial failures. P1: schema_test expects migration version 25 and
attachments table.

P2: Edited remote comments now update local text. Issue renames update
attachments FK. Promote/demote preserves comment external_ref and updated_at.
Attachments FK removed to allow wisp references.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
# Conflicts:
#	cmd/bd/jira.go
#	cmd/bd/linear.go
#	internal/linear/tracker.go
#	internal/storage/dolt/migrations.go
#	internal/storage/storage.go
#	internal/tracker/engine.go
#	internal/tracker/types.go
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.

2 participants