List commands silently truncate at a default limit and never surface total-count or pagination #107
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
fj'slistcommands silently cap results at a default limit and never surface the total count or that more pages exist, so a script cannot tell a complete result set from a truncated one.fj repo listdefaults to 30 (src/cli/repo.rs:309-347handler;--limitdefault 30), yet the server reports the real total. Reproduced against a host with more than 30 repos:The pagination metadata is parsed and then thrown away. Every field of
Page<T>that carries it is dead code (src/client/pagination.rs:5-19):from_headersfaithfully readsX-Total-CountandLink: rel=next(src/client/pagination.rs:38-60), but the list handlers only ever consumepage.items. Inrepo listthe JSON path isserde_json::to_value(&page.items)(src/cli/repo.rs:322-324);totalandnextare never read by any caller, hence the#[allow(dead_code)]on all of them.The default limits compound the trap:
pr list,issue list,repo list, andrelease listall default to 30 (src/cli/pr.rs:77,src/cli/issue.rs:74,src/cli/repo.rs,src/cli/release.rs). And the only pagination escape hatch,--paginate(followLink: rel=nextand concatenate), exists solely onfj api(fj api --help), not on any purpose-builtlistcommand.Why it matters
Silent truncation is the most dangerous failure mode for automation because it looks like success. An agent that runs
fj issue list --jsonto "get all open issues" receives 30, exits 0, and acts on a partial set with no signal that 74 more exist. There is no total in the JSON envelope, nonextcursor, no warning on stderr, and no--paginateto force completeness short of guessing a large--limit.ghsolves exactly this:gh issue list --jsonplus an explicit--limit, and itsapi --paginateis mirrored by list-level paging. For a tool whose headline pitch is JSON "so scripts and AI agents" can consume it, returning a silently-capped slice undercuts the core promise.Possible directions (sketches)
--paginateto thelistcommands (reusing theget_all/Link: rel=nextmachinery already behindfj api --paginateand theopts.limit > 50branch insrc/api/pull_core.rs:117-131), so a script can opt into completeness.--jsonmode, emit an envelope ({ "total_count": N, "items": [...] }) or at least surfaceX-Total-Count, so a consumer can compare returned vs. total and decide whether to page. Thetotalfield is already parsed; it just needs threading through.listis truncated (returned == limit < total), print a dim footer likeShowing 30 of 104. Use --limit or --paginate for more., matching gh's behavior.--limitvalue in each command's help and note that results may be capped.Confidence
High. Truncation reproduced (
fj repo list --json | jq length= 30 withx-total-count: 104on the host), the discard is structural (#[allow(dead_code)]on every metadata field ofPage<T>,src/client/pagination.rs:7-18, and handlers reading onlypage.items, e.g.src/cli/repo.rs:322), and the--paginate/list asymmetry is verified from--help. The envelope-vs-footer choice is a design call.Converted to backlog item
rasterstate/fj#111(p1, size M).Tracked there with task / priority / reason / acceptance / dependencies. Keeping this open with the
convertedlabel as the originating opportunity.Derived backlog item rasterstate/fj#111 is merged. Closing this opportunity per the issue state machine.