Skip to content

Signal a truncated result to --json consumers #1

Description

@kud
branch: main

Idea

jira issue list --json returns a bare array, so a machine consumer cannot tell a complete result from a truncated one. Fifty objects come back whether there were fifty or nine hundred. Emit a truncation signal out of band — a stderr line and a distinct exit code — without changing any JSON shape.

Why

A caller capped this query, got a full page back, and had no way to know rows were missing. Downstream that turned into a silent layout bug rather than a visible error: the consumer rendered a perfectly ordinary-looking result that was quietly incomplete.

The default of 50 is not the defect — it is a sensible default for a human at a terminal. The defect is that the JSON gives a program no way to ask "was that all of it?".

Context

  • src/commands/issue.ts:87.option("-n, --limit <n>", "maximum issues to return", "50"). Same default on search in src/commands/misc.ts:13. Both end in printJson(issues) on a bare array.
  • @kud/jira src/client.ts:156searchIssues is honest: const limit = opts.limit ?? 50, pages at Math.min(100, limit - issues.length), three stop conditions, slice(0, limit). So --limit N genuinely fetches N. The library is not the problem.
  • src/types.ts:84JiraSearchPage is { issues, nextPageToken?, isLast? } with no total. That matches Jira's newer /search/jql, which dropped total counts. So "50 of 900" is not buildable — only "there is more" is. Any design promising a count cannot ship.
  • searchIssues has three call sites, all inside this CLI (issue.ts:92, misc.ts:17, tui/data.ts:61 — that last one passes limit: 200), plus the library's own tests. No external consumers found.

Direction

Minimum honest fix — a stderr warning plus a distinct exit code when issues.length === limit, in issue.ts and misc.ts. No library change, no shape change, byte-identical JSON. Callers that already read exit code and stdout as separate channels consume it with a two-line change. False-positive only on an exact boundary, which is the right side to err on.

Fuller fix, if it earns it — the knowledge exists at the exact moment it is discarded: when searchIssues' while loop exits on its own condition, page.nextPageToken was live and page.isLast was false. Capture it additively — a second export searchIssuesPage(jql, opts): Promise<{ issues, truncated }>, with searchIssues reimplemented as a one-line wrapper returning .issues. Existing contract and tests untouched, minor version.

Explicitly rejected, with reasons — do not reach for these:

  • --all / --limit 0. Looks the most helpful and is the worst. It hands an unbounded pagination loop to a corporate Atlassian behind a VPN, inside callers that wrap this in a timeout — turning a fast degradation into a hang. And it does not fix the defect anyway: an exhausted result is still a bare array with no signal. It moves the failure from silently wrong to silently slow.
  • Changing searchIssues' return type to { issues, truncated }. A breaking change to a published library, breaking three call sites and every test in client.test.ts, to carry one boolean.
  • Changing printJson's shape. Breaks every existing --json consumer.

Check

  • jira issue list -q "<a query with more results than the limit>" -n 5 --json prints five objects on stdout, a warning on stderr, and exits non-zero-but-distinct.
  • The same command under the limit exits 0 with nothing on stderr.
  • Existing --json consumers parse identically in both cases.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

planA captured plan — context and direction for work not yet started

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions