Skip to content

CXP-936; CXP-1010 fix pagination by persisting pageSize on token - #86

Merged
JavierCarnelli-ConductorOne merged 8 commits into
mainfrom
fix/cxp-936
Aug 26, 2026
Merged

CXP-936; CXP-1010 fix pagination by persisting pageSize on token#86
JavierCarnelli-ConductorOne merged 8 commits into
mainfrom
fix/cxp-936

Conversation

@JavierCarnelli-ConductorOne

Copy link
Copy Markdown
Contributor

ListTicketSchemas derived the /project/search page size from the caller's p.Size on every call, but its resume token only recorded a page-relative project index, not the size that index was computed against. C1's driver shrinks the requested page size as ListTicketSchemas accumulates toward its 100-schema cap, so a resume could refetch a smaller window at the same offset than the one the stashed index was valid for: the index landed past the end of the new window (dropping the rest of it), and the offset then advanced by the shrunk window length instead of the true position, causing part of the window to be re-emitted on the next call.

Stash the window size (ProjectPageSize) alongside the resume index so a mid-window resume always refetches the identical window, regardless of what page size the caller sends on that call. A freshly started window (after the prior one is fully consumed) is unaffected and still sizes off the caller's current request.

Fixes CXP-936.

…change

ListTicketSchemas derived the /project/search page size from the caller's
p.Size on every call, but its resume token only recorded a page-relative
project index, not the size that index was computed against. C1's driver
shrinks the requested page size as ListTicketSchemas accumulates toward its
100-schema cap, so a resume could refetch a smaller window at the same
offset than the one the stashed index was valid for: the index landed past
the end of the new window (dropping the rest of it), and the offset then
advanced by the shrunk window length instead of the true position, causing
part of the window to be re-emitted on the next call.

Stash the window size (ProjectPageSize) alongside the resume index so a
mid-window resume always refetches the identical window, regardless of
what page size the caller sends on that call. A freshly started window
(after the prior one is fully consumed) is unaffected and still sizes off
the caller's current request.

Fixes CXP-936.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 24, 2026

Copy link
Copy Markdown

CXP-936

CXP-1010

Comment thread pkg/connector/tickets.go
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Connector PR Review: CXP-936; CXP-1010 fix pagination by persisting pageSize on token

Blocking Issues: 0 | Suggestions: 2 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base eebe2a6a73fa.
Review mode: incremental since b58e001f
View review run

Review Summary

The new commit reworks the ListTicketSchemas resume guard from an if relocated == -1 early return into a three-way switch, adding a default branch that resumes the window from the stashed index (issueTypeIndex = 0, resumedProjectIndex = -1) when the stashed project is gone but the window still holds work. That addresses the prior finding that the rest of the window was silently dropped, and the index-0 test now drains pagination and asserts P2/P3 are actually synced. The full PR diff was also scanned for security and correctness (no dependency manifest changes; go.mod/go.sum untouched); no security issues and no blocking correctness issues were found. Two non-blocking items remain: a narrower residual drop in the outOfBounds branch, and a coverage gap in the new non-zero-index test.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

  • pkg/connector/tickets.go:482-499 — the outOfBounds branch still advances by ProjectOffset + len(projects) and can drop projects that remain unprocessed inside a window shrunk by multiple deletions (same loss class as the bug this commit fixes, narrower trigger).
  • pkg/connector/tickets_test.go:1143-1152TestListTicketSchemas_DropsRestOfWindowWhenDeletedProjectNotFound never asserts that the P1 stashed statuses stay off P2, so a regression in resumedProjectIndex = -1 would still pass; the name also reads as asserting the drop rather than the fix.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

Suggestions:

In `pkg/connector/tickets.go`:
- Around line 482-499: The `case outOfBounds:` branch of the resume-guard switch returns a
  next-page token of `ProjectOffset: tok.ProjectOffset + len(projects)`. `outOfBounds` only
  means `len(projects) <= projectIndex`; when several projects are deleted mid-sync the
  refetched window can still contain projects that were never processed. Example: window size
  8 at offset 0, stashed index 5; originals 0/1/2 plus the stashed project at index 5 are
  deleted, so the refetch returns 4 projects (orig3, orig4, orig6, orig7). outOfBounds is
  true, the code advances to offset 4, and orig6/orig7 are never synced. Consider routing
  every `relocated == -1` case with `len(projects) > 0` into the same handling as the new
  `default` branch (clamp `projectIndex` into range, reset `issueTypeIndex = 0`, set
  `resumedProjectIndex = -1`), and only advancing `ProjectOffset` by `len(projects)` when the
  refetched window comes back empty.

In `pkg/connector/tickets_test.go`:
- Around line 1143-1152: `TestListTicketSchemas_DropsRestOfWindowWhenDeletedProjectNotFound`
  drains pagination and asserts P2/P3 schemas are emitted, but never asserts that the P1
  stashed statuses do not leak onto P2. The `resumedProjectIndex = -1` assignment in the new
  `default` branch is what prevents that leak at a non-zero stashed index, so a regression
  leaving `resumedProjectIndex` at the stale index would still pass this test. Add a check
  over `all` that no schema carries a single status named P1Done, mirroring the assertion in
  `TestListTicketSchemas_DetectsIndexZeroIdentityMismatch`. Also rename the test to something
  like `TestListTicketSchemas_SyncsRestOfWindowWhenDeletedProjectNotFound`, since it asserts
  the rest of the window IS synced rather than dropped.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@JavierCarnelli-ConductorOne
JavierCarnelli-ConductorOne marked this pull request as ready for review August 25, 2026 12:54
@johnallers johnallers self-assigned this Aug 25, 2026
Comment thread pkg/connector/tickets.go Outdated
Comment thread pkg/connector/tickets.go Outdated
issueTypeIndex := tok.IssueTypeIndex
resumedProjectIndex := tok.ProjectIndexInPage

if projectIndex > 0 && projectIndex >= len(projects) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The guard covers the out-of-bounds case, but not the shift case: if a project is removed from the front of the pinned window mid-sync, the refetched window is shorter yet ProjectIndexInPage stays in bounds and now points at a different project. That silently skips one project and, worse, applies the stashed tok.Statuses to the wrong project via the projectIndex == resumedProjectIndex branch (line 502). Pinning a project key/ID alongside the index and verifying it matches on resume (falling back to a scan or a window advance) would make the resume robust to any window mutation, not just shrinkage past the end.

Confidence: medium — requires a project to be deleted or become inaccessible mid-sync.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going way out of scope. I created this ticket for this: CXP-1010

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

@JavierCarnelli-ConductorOne JavierCarnelli-ConductorOne changed the title CXP-936 fix pagination by persisting pageSize on token CXP-936; CXP-1010 fix pagination by persisting pageSize on token Aug 26, 2026
A project deleted or made inaccessible mid-sync can shift the pinned
project window without the stashed resume index going out of bounds,
silently skipping a project and misapplying its cached statuses to
whatever project now sits at that index. Stash the project key
alongside the index and relocate by key on resume instead of trusting
position alone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@JavierCarnelli-ConductorOne
JavierCarnelli-ConductorOne dismissed github-actions[bot]’s stale review August 26, 2026 03:35

Requires a review on the extended fix

Comment thread pkg/connector/tickets.go Outdated
Comment thread pkg/connector/tickets.go Outdated
Comment thread pkg/connector/tickets.go

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

The identity check only ran for projectIndex > 0, so a stashed resume
at index 0 (cap hit mid-first-project) never verified the window still
matched, letting stale statuses apply to a different project. Also
switch the stashed identifier from project.Key (user-renamable) to
project.ID (immutable) to avoid false mismatches on rename.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread pkg/connector/tickets.go Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

The relocation guard treated "no match found" as "nothing left to
process" and always skipped past the whole window, even when other,
untouched projects still sat at or after the stashed index. Only take
that shortcut when the index is actually out of bounds; otherwise
resume the window from the current index as if visiting it fresh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@JavierCarnelli-ConductorOne
JavierCarnelli-ConductorOne dismissed github-actions[bot]’s stale review August 26, 2026 05:03

Requires a new review, once again

Comment thread pkg/connector/tickets.go
Comment thread pkg/connector/tickets_test.go

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@JavierCarnelli-ConductorOne
JavierCarnelli-ConductorOne merged commit 23f5c4b into main Aug 26, 2026
9 checks passed
@JavierCarnelli-ConductorOne
JavierCarnelli-ConductorOne deleted the fix/cxp-936 branch August 26, 2026 11:50
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.

3 participants