Skip to content

GH-91: feat(github): implement SyncCapable — snapshots, delta listing, field writes, idempotent comments#95

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

GH-91: feat(github): implement SyncCapable — snapshots, delta listing, field writes, idempotent comments#95
alekspetrov merged 1 commit into
mainfrom
pilot/GH-91

Conversation

@alekspetrov

Copy link
Copy Markdown
Contributor

Summary

Automated PR created by Pilot for task GH-91.

Closes #91

Changes

GitHub Issue GH-91: feat(github): implement SyncCapable — snapshots, delta listing, field writes, idempotent comments

Context

SaaS pre-work S0.8 (kanban sync — design: qf-studio/pilot .agent/system/saas-kanban-sync-design.md §2). The SyncCapable contract landed in #83. This issue implements it for GitHub.

Depends on: #86

Implementation

In sdk/integrations/github, implement core.SyncCapable:

  • ListUpdatedSince: REST GET /repos/{o}/{r}/issues?since=&sort=updated&direction=asc&state=all&per_page=100 with the fix(github): exhaustive pagination for ListIssues/ListReleases/ListTags (30-item silent truncation) #86 pagination pattern; map to IssueSnapshot (StateGroup = open/closed; Priority via core.NormalizePriority from labels if present; SequenceID = bare number rendered per the existing provider-prefix scheme).
  • ListAll: exhaustive issue listing (state=all), same mapping.
  • GetIssue: single-issue fetch by number.
  • UpdateFields: PATCH title/body/labels (FieldPatch keys: title, body, labels).
  • TransitionState: close/reopen (provider states "open"/"closed").
  • AddComment: embed <!-- pilot-op:{idemKey} --> in the body; before posting on retry, scan existing comments for the marker (idempotency per design §3.4).
  • CreateIssue from IssueDraft.
    Exclude PRs from issue listings (GitHub returns PRs in /issues — filter pull_request key).

Acceptance

  • var _ core.SyncCapable = (*Client)(nil) (or the wrapper type) compiles
  • Table-driven tests with mocked REST: multi-page ListUpdatedSince honoring since; PR filtering; idempotent AddComment (marker found → no repost); UpdateFields partial patch
  • No change to existing Poller/IssueEvent behavior; api.golden untouched (integrations are outside the core lock)

Refs

… writes, idempotent comments

Adds SyncClient (owner/repo-bound, mirroring Poller) implementing
core.SyncSource and core.SyncWriter: exhaustive cursor-paginated
ListUpdatedSince/ListAll (state=all, sort=updated, direction=asc,
per_page=100, PR-filtered), GetIssue, partial UpdateFields
(title/body/labels), TransitionState, idempotent AddComment via a
pilot-op HTML comment marker, and CreateIssue.
@alekspetrov
alekspetrov merged commit e4db871 into main Jul 13, 2026
3 checks passed
@alekspetrov
alekspetrov deleted the pilot/GH-91 branch July 13, 2026 18:39
alekspetrov added a commit that referenced this pull request Jul 13, 2026
…esolveRepo write-scope consistency (post-merge review of #95) (#100)

Post-merge review of #95 found two correctness defects, both fixed in
sdk/integrations/github/sync.go:

- AddComment's idempotency scan now paginates exhaustively (per_page=100,
  no cap) instead of relying on Client.ListIssueComments' documented
  single default-sized page — a marker beyond the first page was
  previously invisible to a retry, causing a duplicate repost.
- resolveRepo now errors on a malformed projectID instead of silently
  falling back to the bound repo, and IssueSnapshot.NativeID is
  repo-qualified ("owner/repo#N") whenever a listing resolves to a repo
  other than the bound one. GetIssue/UpdateFields/TransitionState/
  AddComment decode that qualifier via parseNativeID, so a write-back
  after listing a non-bound projectID targets the correct repo instead
  of silently hitting the bound repo's same-numbered issue.

Also: since is normalized to UTC before formatting (a non-UTC "+02:00"
offset embeds a literal "+" that decodes as a space server-side), and
issuesSyncPerPage documents that the exhaustive cursor loop has no page
cap by design (host's responsibility), unlike the capped board-scale
listings elsewhere in this package.
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.

feat(github): implement SyncCapable — snapshots, delta listing, field writes, idempotent comments

1 participant