Implements `pr list` with the two policies later PR slices reuse: client-side filtering with the filtered-set count line (ADR 0005) and the official-first reviewDecision via parallel per-PR review fetches (ADR 0006), extracted into src/review.ts. API-supported flags map to their params (--state, --author→poster, --label name→ID, --label-id, --sort, --limit, --fields); --assignee, --base, --head, and --draft filter in-process after full pagination, with the count line's total taken from the filtered set. --search is refused with a redirect to `search prs`. Adds a boolText field extractor and a shared parsePositiveInt helper, the latter also adopted by issue list's --limit parsing.
3.5 KiB
spec, blocked-by
| spec | blocked-by |
|---|---|
| gitea-axi | 0004-issue-create-and-comment |
What to build
pr list, introducing two policies that later slices reuse: client-side filtering with its count-line rule (see ADR 0005) and the reviewDecision computation via parallel review fetches (see ADR 0006).
API-supported flags: --state, --author (maps to poster), --label (name→ID via the case-insensitive label lookup, since the PR list endpoint takes label IDs), --label-id (Gitea-specific bypass), --sort (Gitea-specific values passed straight to the API), --limit, --fields.
Client-side filters (no API param exists): --assignee, --base, --head, --draft — each paginates fully at 50 per page and filters in-process, with the count line's T computed from the filtered set instead of the misleading X-Total-Count.
Default fields: number, title, state, author, draft (bool→yes/no), review — the reviewDecision mapped to approved/changes_requested/required.
reviewDecision uses the official-first fallback: only official reviews count when any exist, otherwise all reviews; CHANGES_REQUESTED beats APPROVED, non-stale non-dismissed approval wins, everything else is required; there is no none value.
--search is forbidden with a redirect to search prs.
Acceptance criteria
pr listrenders the default fields withreviewcomputed from one parallel review fetch per PR- reviewDecision honors the official-first fallback and maps to the three lowercase values, with zero-review and comment-only PRs rendering
required --labelresolves the name case-insensitively to an ID (VALIDATION_ERRORif unknown);--label-idbypasses the lookup--authorand--sortmap to their API params;--sortaccepts the six Gitea values--assignee,--base,--head, and--draftfilter client-side after full pagination, and the count line reportscount: N of T totalwithTfrom the in-memory filtered set--fieldsexposesbody,createdAt,labels,milestone,mergedAt,url--searchfails withVALIDATION_ERROR(exit 2) pointing atgitea-axi search prs "<query>"- Empty result emits
pull_requests[0]: (none)plus a suggestion - Fixture-server tests cover the review computation variants (official/unofficial, stale, dismissed), each client-side filter with its count line, the label lookup, and the forbidden flag
Implementation Notes
- The
reviewDecisioncomputation lives in a newsrc/review.tsmodule (reviewDecisionpure core +fetchReviewDecisionI/O shell), sopr viewand the dashboard can reuse the same policy in later slices (ADR 0006). - Added a
boolTextfield extractor tosrc/fields.tsfor thedraftbool→yes/no column; it is part of the shared field vocabulary rather than inlined, sincepr viewrendersdrafttoo. - Extracted
parsePositiveIntintosrc/flags.tsand routedpr list's--limit/--label-idandissue list's--limitthrough it, collapsing three copies of the same positive-integer parse into one (a review finding). Behaviour and error wording are unchanged. - Small unrequested robustness kept deliberately:
--label-idaccepts a comma-separated list (mirroring--label), and--label+--label-idmay be combined — their resolved IDs concatenate. The spec describes each as a single value; this is a strict superset with no behaviour change for the single-value case. - The
urlextra field pluckshtml_url(the browsable URL), matchingissue list's precedent rather than Gitea's APIurlfield.