Repository navigation
feat: honor repeated --label and paginate past 50 results - #38
Closed
seraph-pixelperfect wants to merge 1 commit into
Closed
seraph-pixelperfect wants to merge 1 commit into
seraph-pixelperfect wants to merge 1 commit into
Conversation
Honor every repeated --label via labels: { some: { name: { in: [...] } } }
(any-of semantics, documented in --help) and auto-paginate fetchIssues in
50-size batches using pageInfo { hasNextPage endCursor } until --limit is
satisfied. Raise the --limit cap to 500 (rejected loudly above it), keep the
truncation hint accurate via pageInfo.hasNextPage, and drop the old
first-label-only filter + hint.
Closes #18
This was referenced Aug 19, 2026
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #37 (
feat/issues-search) — review that one first. This branch adds a single commit (230c540) on top ofe1ef299.Closes #18. Depends on #37.
Two behaviors land together, per the issue:
--labelis now honored vialabels: { some: { name: { in: [...] } } }("any of" semantics: an issue matches when it carries at least one of the given labels). The old single-labelfilter.label(labels: { name: { eq: ... } }) and the "Only the first label was applied — multi-label filter lands post-v1" hint are removed. The filter composes into thesearchIssuesquery too, so--search+ multiple labels work together.fetchIssuesnow requestspageInfo { hasNextPage endCursor }and follows the cursor in 50-size batches until--limitis satisfied or the server reports no more pages. Each page asks for only the remaining slots (min(limit - collected, 50)), so we never over-fetch.--limitcap raised from 50 to 500.Verified comparator shape (schema source)
labels: { some: { name: { in: [...] } } }matches the live schema exactly — no deviation from the issue's stated shape was needed. Verified against the generated GraphQL type documents in@linear/sdk@90.0.0(npm pack @linear/sdk, thendist/index-zxW4m1xd.d.mts):IssueFilter.labels?: IssueLabelCollectionFilter— "Filters that issue labels must satisfy."IssueLabelCollectionFilter.some?: IssueLabelFilter— "Filters that needs to be matched by some issue labels." (everyalso exists;someis the correct "any of" combinator, so the issue's shape is schema-valid as written.)IssueLabelFilter.name?: StringComparatorStringComparator.in?: Array<String>— "In-array constraint."Pagination support was verified the same way:
SearchIssuesQueryVariablesincludesafter?: Stringalongsidefirst?: Int, and bothIssueConnectionandIssueSearchPayloadexposepageInfo: PageInfo(hasNextPage: Boolean!,endCursor?: String). So--searchresults paginate through the same loop as plainissues.Demo (real commands, this branch)
Help text documents the new semantics and cap:
Before/after — the exact query this branch sends for
--label a --label b --limit 200(debug dump viaLINEAR_AXI_DEBUG=1, reaching the real API layer; 401 is expected with a fake key):Before (base
e1ef299): the same command sentfilter: { labels: { name: { eq: "bug" } } }— first label only, noafter, nopageInfo, hardfirst: min(limit, 50)— and printed a hint that multi-label lands post-v1.Limit enforcement is loud, not silent:
Local gates (all run on this branch)
63 = 49 pre-existing (all still passing) + 12 new in
test/issues-pagination.test.ts+ 2 newhasMorecases intest/format.test.ts. No test was deleted.format:checkis the known pre-existing red (same 18 files as on the base branch; prettier 3.9.6 drift, not run in CI). New/edited files that were clean before (src/format.ts,src/skill.ts,test/format.test.ts,test/issues-search.test.ts) remain clean, and the newtest/issues-pagination.test.tspassesprettier --check.One existing test was updated in place, not weakened:
test/issues-search.test.ts("composes search with team + state + label") used the removedIssueListFilter.labelkey and asserted the oldlabels: { name: { eq: "bug" } }shape; it now useslabels: ['bug']and assertslabels: { some: { name: { in: ["bug"] } } }. This mirrors the deliberate behavior change in this PR.Design notes & edge cases flagged
--limit > 500: chose loud rejection (exit 2,VALIDATION_ERROR) over silent clamping, per AXI principle 6 (fail loud) — consistent with how the CLI treats unknown flags and blank flag values. Help text says "max 500" rather than "capped at" to match.hasNextPage: true: if the API returns fewer nodes than requested while saying more exist, we keep paging (a short page is not exhaustion — Linear may legitimately return partial pages). Covered by a test.endCursoris missing/non-advancing whilehasNextPagestays true, we stop rather than loop forever;hasMoreretains the server's claim, so the truncation hint still shows.fetchIssuesnow returnshasMore(last page'shasNextPage) andformatCountLineaccepts it. WhenhasMore: falseandcount === limit, the "(showing first N)" hint is suppressed — exactly-N matches is not truncation.hasMore: undefinedfalls back to the oldcount === limitheuristic, so other callers are unaffected.totalCountnot used:IssueSearchPayloaddoes exposetotalCount, but the plainissuesconnection does not, and mixing count-line styles between--searchand normal listing would be inconsistent — kept the slice-based count +hasMorehint everywhere.skills/linear-axi/SKILL.mdwas regenerated (pnpm run build:skill) after updating one tip line insrc/skill.tsto state the any-of semantics; the--checkgate passes.Stacked-PR / CI notes
issues --searchfull-text search #37 (feat/issues-search) and targetsfeat/issues-search, notmain. GitHub will show no CI checks for it becauseci.ymlonly triggers on PRs targeting main — CI will run when this PR is retargeted to main after feat: addissues --searchfull-text search #37 merges; the local gates pasted above are the evidence. It should not be retargeted as part of this review.gh pr checksrenders as a failure. If a check looks red, inspectgh run view <run-id>/ the jobs API for an emptystepsarray and a ~3-4s duration before assuming breakage; if quota-blocked, treat the local output above as authoritative. Jobs are not retried.seraph-pixelperfect, for human review — not self-approved. Merge is a human decision.