# list_prs_needs_review_not_stale — Open PRs With No Approvals, Up-to-Date

**Script name:** `list_prs_needs_review_not_stale`

**Script location:** [`scripts/list_prs_needs_review_not_stale.ts`](../../../scripts/list_prs_needs_review_not_stale.ts)

**Invoke:**
```bash
npx --yes tsx .opencode/skills/auto-agents-system/scripts/list_prs_needs_review_not_stale.ts \
  --url URL --pat TOKEN --owner OWNER --repo REPO

**CRITICAL:** Always invoke the script using bash and the format noted above. **Never** try to use the skill as a task, or invoke it through any other method than by directly calling bash with a command of the form listed above under "Invoke:".
```


## Synopsis

```
list_prs_needs_review_not_stale.ts --url URL --pat TOKEN --owner OWNER --repo REPO
```

Returns all **open** pull requests that have **zero non-dismissed approvals**
and are **not stale** — i.e. the PR's `merge_base` already matches the current
HEAD of its base branch. The PR is fully up-to-date and requires no rebase
before merging.

These are the simplest PRs in the "needs review" set: once a reviewer approves
them, they can be merged immediately without any additional rebase or conflict
resolution step.

This script is a thin wrapper around [`list_prs`](../list_prs/). It
hard-codes the following filters and delegates all logic to that script:

```
state:        'open'
maxApprovals: 0
stale:        'not_stale'
(no ciStatus filter — all CI states are included)
```


## Arguments

All four arguments are **required**. No optional filter arguments exist — the
filters are baked in and cannot be changed.

| Flag | Type | Description |
|------|------|-------------|
| `--url URL` | string | Forgejo base URL, **no trailing slash** (e.g. `https://git.example.com`) |
| `--pat TOKEN` | string | Personal Access Token with read access to the repository |
| `--owner OWNER` | string | Repository owner or organisation name |
| `--repo REPO` | string | Repository name |
| `-h`, `--help` | — | Print usage to stderr and exit 0 |

Any flag other than these five is **rejected** with exit code 1 and an error
message: `ERROR: Unknown argument: <flag>`. Filter parameters are hard-coded
and cannot be overridden via CLI flags on this wrapper script.

### Invalid Argument Combinations

There are no valid filter-combination errors since no optional filters exist.
However, passing any filter-style flag (e.g. `--min-approvals`, `--stale`, etc.)
will cause an immediate exit 1 — those flags are not accepted by this wrapper.


## Hard-Coded Filters

| Filter | Value | Meaning |
|--------|-------|---------|
| `state` | `open` | Only open PRs are returned |
| `maxApprovals` | `0` | PRs with zero non-dismissed `APPROVED` reviews |
| `stale` | `not_stale` | `PR.merge_base` equals the current base branch HEAD |

**"Not stale"** means `PR.merge_base == current base branch HEAD`: the base
branch has not advanced since the PR's head was last reconciled, so no rebase
is needed.

**"Zero approvals"** means: no reviewer has an active (non-dismissed) `APPROVED`
review as their most recent review. A reviewer who approved and then later
submitted `REQUEST_CHANGES` is not counted as an approver.


## Output

Written to **stdout** as a pretty-printed JSON array sorted by `priority_rank`
(highest priority first). Each element is the full Forgejo PR object augmented
with four fields:

| Field | Type | Description |
|-------|------|-------------|
| `approvals_count` | integer | Will always be `0` for results of this script |
| `stale_state` | string | Will always be `not_stale` for results of this script |
| `ci_status` | string | `passing`, `failing`, `pending`, or `unknown` — **not filtered**; any CI state is included |
| `priority_rank` | integer | 0 (highest) through 6 (unlabelled) |
| `priority_label` | string \| null | Matched `Priority/*` label name, or `null` |

An **empty array** `[]` is a valid and common result — it means all open
up-to-date PRs already have at least one approval.

See [`list_prs`](../list_prs/) for the full output schema documentation.


## Diagnostics / stderr

Progress messages are written to stderr and do not affect the JSON output:

```
Fetching PRs from https://git.example.com/org/repo (state=open)...
Fetched 42 PR(s).  Enriching and filtering...
Fetching HEAD SHAs for 3 unique base branch(es)...
Fetching reviews for 42 PR(s) concurrently (limit=20)...
Fetching CI status for 42 PR(s) concurrently (limit=20)...

Result: 8/42 PR(s) matched the filter criteria.
```


## Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success — JSON array written to stdout (may be `[]`) |
| 1 | Fatal error — message written to stderr |


## Errors the Script May Throw

| Condition | Behaviour |
|-----------|-----------|
| Missing `--url`, `--pat`, `--owner`, or `--repo` | Exit 1: `ERROR: --<flag> is required` |
| Any unknown flag passed | Exit 1: `ERROR: Unknown argument: <flag>` |
| Forgejo API non-2xx on PR list | Exit 1 with HTTP status and body detail |
| Forgejo API failure for a single branch or review | Warning to stderr; processing continues |
| Network timeout (30 s per request) | Exit 1 with error message |


## When to Use This Script

Use `list_prs_needs_review_not_stale` when you want to identify PRs that are
**waiting for a first review and are already current with their base branch**.
Typical use cases:

- **`pr-review-pool-supervisor`** uses this as its highest-priority review
  bucket: once approved, these PRs merge cleanly with no further action.
- A **review dispatcher** agent can prioritise these over stale PRs since
  reviewing them provides immediate merge readiness.
- A **monitoring agent** can track this bucket to detect a backlog of fresh
  PRs waiting for review.

### Position in the Six-Bucket System

```
Every open PR belongs to exactly one bucket at any moment:

  Bucket 1 — list_prs_ready_to_merge               (approved + not stale)
  Bucket 2 — list_prs_stale_clean                   (approved + stale, no conflicts)
  Bucket 3 — list_prs_stale_conflicts               (approved + stale, has conflicts)
  Bucket 4 — list_prs_needs_review_not_stale        (no approvals + not stale)       ← THIS SCRIPT
  Bucket 5 — list_prs_needs_review_stale_clean      (no approvals + stale, no conflicts)
  Bucket 6 — list_prs_needs_review_stale_conflicts  (no approvals + stale, has conflicts)

Exception: PRs with stale_state == stale_unknown are transiently absent from
all buckets while Forgejo computes conflict status. They appear shortly.
```

Workflow for a PR in this bucket once approved:
```
Reviewer approves  →  PR moves to list_prs_ready_to_merge  →  merge_pr
```


## Usage Examples

```bash
# Standard usage
npx --yes tsx /app/.opencode/skills/auto-agents-system/scripts/list_prs_needs_review_not_stale.ts \
  --url https://git.example.com \
  --pat ghp_XXXX \
  --owner my-org \
  --repo my-repo

# Count how many fresh PRs are waiting for a first review
npx --yes tsx /app/.opencode/skills/auto-agents-system/scripts/list_prs_needs_review_not_stale.ts \
  --url https://git.example.com \
  --pat ghp_XXXX \
  --owner my-org \
  --repo my-repo \
  | jq 'length'

# Get just the PR numbers and titles
npx --yes tsx /app/.opencode/skills/auto-agents-system/scripts/list_prs_needs_review_not_stale.ts \
  --url https://git.example.com \
  --pat ghp_XXXX \
  --owner my-org \
  --repo my-repo \
  | jq '[.[] | {number, title}]'

# Get only the highest-priority PR needing review
npx --yes tsx /app/.opencode/skills/auto-agents-system/scripts/list_prs_needs_review_not_stale.ts \
  --url https://git.example.com \
  --pat ghp_XXXX \
  --owner my-org \
  --repo my-repo \
  | jq 'first'
```


## Agent Usage Examples

### pr-review-pool-supervisor

The supervisor can call this script to find its highest-priority review
targets — PRs that will merge immediately after approval. It dispatches
`pr-reviewer` workers for each result.

```bash
npx --yes tsx /app/.opencode/skills/auto-agents-system/scripts/list_prs_needs_review_not_stale.ts \
  --url $FORGEJO_URL \
  --pat $FORGEJO_PAT \
  --owner $REPO_OWNER \
  --repo $REPO_NAME
```

### pr-merge-pool-supervisor

The merge supervisor uses this to surface PRs waiting for review that will
have the simplest post-approval workflow. An empty result here (combined with
non-empty stale buckets) suggests review bandwidth is being absorbed by PRs
that still have pending rebase work.

### human-liaison-pool-supervisor

The human liaison can filter by `created_at` to identify PRs that have been
waiting for a first review for too long, then post reminder comments or alert
human reviewers.


## Dependencies

- Node.js ≥ 18 (native `fetch` API)
- `npx tsx` for TypeScript execution
- Zero external npm packages
