|
| 1 | +package core |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "time" |
| 6 | +) |
| 7 | + |
| 8 | +// IssueSnapshot is the normalized, point-in-time view of a single tracker |
| 9 | +// issue used by the board-sync engine. A connector's SyncSource produces |
| 10 | +// snapshots; the host diffs each snapshot against a stored "shadow" (the |
| 11 | +// last-synced copy) to compute a 3-way merge between the provider, the |
| 12 | +// shadow, and the host's own board state. |
| 13 | +type IssueSnapshot struct { |
| 14 | + // NativeID is the provider's opaque internal identifier for the issue. |
| 15 | + NativeID string |
| 16 | + // SequenceID is the provider-prefixed human identifier (e.g. "GH-83"). |
| 17 | + // The prefix scheme is load-bearing: Pilot's branch naming |
| 18 | + // (pilot/<SequenceID>) depends on it staying stable across syncs. |
| 19 | + SequenceID string |
| 20 | + Title string |
| 21 | + Body string |
| 22 | + // State is the provider-native state id or name (e.g. a workflow state). |
| 23 | + State string |
| 24 | + // StateGroup is the provider's own state category (e.g. "todo", "in |
| 25 | + // progress", "done") for providers that expose one; empty otherwise. |
| 26 | + StateGroup string |
| 27 | + Labels []string |
| 28 | + // Priority is normalized via NormalizePriority so the shadow diff |
| 29 | + // compares one vocabulary across every provider. |
| 30 | + Priority string |
| 31 | + // Assignee is a display-only identifier; it is not resolved to a host |
| 32 | + // user record here. |
| 33 | + Assignee string |
| 34 | + URL string |
| 35 | + |
| 36 | + CreatedAt time.Time |
| 37 | + UpdatedAt time.Time |
| 38 | + |
| 39 | + // Deleted marks that the issue was removed or archived on the provider |
| 40 | + // side since the last sync; the shadow diff treats this as a tombstone. |
| 41 | + Deleted bool |
| 42 | +} |
| 43 | + |
| 44 | +// Cursor is an opaque, provider-specific pagination token returned by |
| 45 | +// SyncSource list calls and passed back on the next page request. |
| 46 | +type Cursor string |
| 47 | + |
| 48 | +// FieldPatch is a partial set of issue fields to apply via |
| 49 | +// SyncWriter.UpdateFields. Keys are field names understood by the target |
| 50 | +// connector; values are the new field content. |
| 51 | +type FieldPatch map[string]any |
| 52 | + |
| 53 | +// IssueDraft is the minimal payload needed to create a new issue on a |
| 54 | +// provider via SyncWriter.CreateIssue. |
| 55 | +type IssueDraft struct { |
| 56 | + Title string |
| 57 | + Body string |
| 58 | + Labels []string |
| 59 | + Priority string |
| 60 | +} |
| 61 | + |
| 62 | +// SyncSource is implemented by connectors that can read issue state for |
| 63 | +// board synchronization. Unlike Pollable/Poller, it is not trigger-label |
| 64 | +// filtered and is not restricted to created|updated actions — it exposes |
| 65 | +// the full issue set a shadow-based sync needs to reconcile. |
| 66 | +type SyncSource interface { |
| 67 | + // ListUpdatedSince returns issues changed on or after since, paginated |
| 68 | + // via page (empty Cursor requests the first page). It returns the page |
| 69 | + // of snapshots and the cursor for the next page (empty when exhausted). |
| 70 | + ListUpdatedSince(ctx context.Context, projectID string, since time.Time, page Cursor) ([]IssueSnapshot, Cursor, error) |
| 71 | + |
| 72 | + // ListAll returns every issue in projectID, paginated via page. Used for |
| 73 | + // full-resync passes where an incremental cursor is unavailable or |
| 74 | + // distrusted. |
| 75 | + ListAll(ctx context.Context, projectID string, page Cursor) ([]IssueSnapshot, Cursor, error) |
| 76 | + |
| 77 | + // GetIssue fetches a single issue snapshot by its native ID. |
| 78 | + GetIssue(ctx context.Context, nativeID string) (IssueSnapshot, error) |
| 79 | +} |
| 80 | + |
| 81 | +// SyncWriter is implemented by connectors that can write issue state back |
| 82 | +// to the provider as part of board synchronization. |
| 83 | +type SyncWriter interface { |
| 84 | + // UpdateFields applies a partial field patch to nativeID and returns the |
| 85 | + // resulting snapshot. |
| 86 | + UpdateFields(ctx context.Context, nativeID string, fields FieldPatch) (IssueSnapshot, error) |
| 87 | + |
| 88 | + // TransitionState moves nativeID to providerState, a provider-native |
| 89 | + // state id or name (see IssueSnapshot.State). |
| 90 | + TransitionState(ctx context.Context, nativeID, providerState string) error |
| 91 | + |
| 92 | + // AddComment posts body as a comment on nativeID. idemKey is a caller- |
| 93 | + // supplied idempotency key so retried syncs do not double-post. |
| 94 | + AddComment(ctx context.Context, nativeID, body, idemKey string) error |
| 95 | + |
| 96 | + // CreateIssue creates a new issue in projectID from draft and returns |
| 97 | + // the resulting snapshot. |
| 98 | + CreateIssue(ctx context.Context, projectID string, draft IssueDraft) (IssueSnapshot, error) |
| 99 | +} |
| 100 | + |
| 101 | +// SyncCapable is implemented by connectors that support full board |
| 102 | +// synchronization: reading provider issue state (SyncSource) and writing |
| 103 | +// changes back (SyncWriter). No connector implements this yet — the types |
| 104 | +// here are the contract; connector implementations are follow-up work. |
| 105 | +type SyncCapable interface { |
| 106 | + SyncSource |
| 107 | + SyncWriter |
| 108 | +} |
0 commit comments