Skip to content

GH-96: fix(linear): SyncAdapter correctness — TransitionState name→id, paginated idem comment scan, on-or-after delta (post-merge review of #94)#99

Merged
alekspetrov merged 1 commit into
mainfrom
pilot/GH-96
Jul 13, 2026
Merged

GH-96: fix(linear): SyncAdapter correctness — TransitionState name→id, paginated idem comment scan, on-or-after delta (post-merge review of #94)#99
alekspetrov merged 1 commit into
mainfrom
pilot/GH-96

Conversation

@alekspetrov

Copy link
Copy Markdown
Contributor

Summary

Automated PR created by Pilot for task GH-96.

Closes #96

Changes

GitHub Issue GH-96: fix(linear): SyncAdapter correctness — TransitionState name→id, paginated idem comment scan, on-or-after delta (post-merge review of #94)

Context

PR #94 (Linear SyncCapable, issue #92) merged with three correctness defects found in post-merge review (2026-07-13). Nothing consumes SyncCapable yet (sync engine is a later milestone), so fixes are non-breaking.

Implementation

All in sdk/integrations/linear/sync.go:

  1. TransitionState name→id resolution (round-trip broken today): IssueSnapshot.State carries the workflow state NAME, but TransitionState (sync.go:332) passes providerState straight to Client.UpdateIssueState, which sends GraphQL stateId — writing back a read state name fails the mutation. Resolve name→state-UUID via a per-team workflow-state lookup (pattern: resolveTeamID / GetTeamDoneStateID), accept BOTH a state id and a name, and add a test for the name path.
  2. AddComment idempotency scan must paginate: SyncIssueComments (sync.go:349-357) fetches only the default comments page (~50); a pilot-op marker beyond it is missed → duplicate repost on retry. Follow the comments connection cursor (or newest-first with explicit cap + doc). Add a multi-page idempotency test.
  3. Delta semantics must match the core contract (on-or-after): implementation uses updatedAt: { gt: $since } with RFC3339 second truncation — an issue updated exactly at the whole-second watermark is silently dropped (lost-update class). Switch to gte + time.RFC3339Nano (UTC); callers dedupe by NativeID. Update the method doc comment in lockstep.
  4. (From review nits, cheap while in there): UpdateFields must return an error for unrecognized keys / wrongly-typed values instead of silently dropping them and returning a fresh snapshot as if applied.

Acceptance

Refs

Planned Steps (execute all in sequence)

  • Step 1: fix(linear): SyncAdapter correctness — state name→id, paginated idem scan, on-or-after delta, UpdateFields validation — Fix all four post-merge review defects in sdk/integrations/linear/sync.go as one atomic change (single file, tightly coupled tests): (1) TransitionState name→id — accept both state UUID and state name in TransitionState; add a per-team workflow-state lookup (following the resolveTeamID / GetTeamDoneStateID pattern already in the package) to resolve name→UUID before calling Client.UpdateIssueState; keep the UUID path fast by skipping lookup if input already looks like a UUID. (2) Paginated idempotency scan — in SyncIssueComments (sync.go:349-357), follow the comments connection cursor to scan all pages for the pilot-op marker (or newest-first with an explicit documented cap) to prevent duplicate reposts when the marker sits beyond the first page. (3) On-or-after delta — switch the delta query from updatedAt: { gt: $since } to { gte: $since } and format the timestamp as time.RFC3339Nano in UTC; update the ListChangedSince doc comment to state on-or-after semantics and note that callers dedupe by NativeID. (4) UpdateFields validation — return a wrapped error for unrecognized field keys or wrongly-typed values instead of silently ignoring them, with no partial application on error. Add tests in the same package's _test.go: TransitionState succeeds with a state UUID input; TransitionState succeeds with a state name input (mocked workflow-state resolution); AddComment idempotent when the marker is on page 2 (mocked multi-page comments connection); Delta returns an issue whose updatedAt equals since exactly; UpdateFields with unknown key returns an error and mutates nothing; all existing GH-92: feat(linear): implement SyncCapable — cursor-followed delta, taxonomy StateGroup, field writes #94 tests remain green and sdk/core/api.golden stays untouched.

…scan, on-or-after delta, UpdateFields validation

Post-merge review of #94 found four defects in sdk/integrations/linear/sync.go:
TransitionState sent a state NAME straight through as a GraphQL stateId
(round-trip broken); AddComment's idempotency scan only checked the first
comments page, letting retries duplicate-post once a marker fell off page 1;
ListUpdatedSince used gt+second-truncated timestamps, silently dropping issues
updated exactly at the watermark; and UpdateFields silently ignored unknown
keys and wrongly-typed values instead of rejecting the patch.
@alekspetrov
alekspetrov merged commit de93ca3 into main Jul 13, 2026
3 checks passed
@alekspetrov
alekspetrov deleted the pilot/GH-96 branch July 13, 2026 19:00
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.

fix(linear): SyncAdapter correctness — TransitionState name→id, paginated idem comment scan, on-or-after delta (post-merge review of #94)

1 participant