Skip to content

fix(core): plugin vcs.branch.list has no default or ceiling — no-limit and limit:0 return every branch, fractional/negative limits fail the git call #945

Closed

Description

@dezren39

Problem

The plugin host's vcs.branch.list passes a plugin-supplied limit through to git for-each-ref --count= untouched — no default, no cap, and no integer validation. The HTTP endpoint it mirrors applies all three. The sibling divergence for session.list was filed as #935; this is the same class on the VCS surface, with an extra failure mode: non-integer limits make the git call itself fail.

Verified on main 3a1c3a0c78:

Plugin limit Plugin host result HTTP vcs.branch.list result
omitted every local + remote branch (no --count argument at all) newest 50
10000 --count=10000 honoured verbatim capped at 100
0 falsy in the adapter, so --count is omitted → every branch (asking for none returns all) rejected: query schema requires a positive integer
-5 --count=-5 → git for-each-ref usage error, exit 129 (verified on laptop) rejected: positive integer only
1.5 --count=1.5 → error: option 'count' expects an integer value (verified on laptop) rejected: positive integer only

Evidence (file:line, main 3a1c3a0)

  • packages/core/src/plugin/host.ts:547-549 — branch.list: (input) => response(vcs.branches({ search: input?.search, limit: input?.limit })): the caller's limit passes through untouched.
  • packages/core/src/plugin/vcs/git.ts:261-276 — branches() builds --count=${options.limit} whenever options?.limit is truthy (line 270); an undefined or 0 limit omits --count entirely, so for-each-ref returns every ref.
  • packages/server/src/handlers/vcs.ts:87 — the HTTP handler the plugin surface mirrors applies Math.min(ctx.query.limit ?? 50, 100): default 50, ceiling 100.
  • packages/protocol/src/groups/vcs.ts:21-25 — the HTTP query schema restricts limit to PositiveInt; the plugin input (packages/plugin/src/effect/vcs.ts:14-17, packages/plugin/src/promise/vcs.ts:13) is a bare optional number with no such restriction.
  • Laptop verification of the git failure modes: git for-each-ref --count=-5 → usage error, exit 129; --count=1.5 → error: option 'count' expects an integer value with an optional k/m/g suffix; --count=0 → all refs (0 means no limit to git, matching the adapter's falsy-omission behaviour).

Dedup: gh issue list --search "vcs branch limit plugin" and --search "branch.list" return nothing covering this; #935 covers session.list only, a different surface and code path.

Why it matters

  • The two surfaces of the same endpoint disagree silently: a plugin paging branches with the documented HTTP behaviour in mind gets the whole ref set on page one, and on a long-lived repo with thousands of remote branches that is an unbounded list materialised through the plugin host.
  • limit: 0 inverting to "return everything" is the worst case of the divergence — the smallest request produces the largest response.
  • A fractional or negative limit (easy for a plugin computing a page size dynamically) doesn't degrade to a default; it fails the whole for-each-ref call with a git usage error surfaced to the plugin.

Acceptance criteria

  • Plugin vcs.branch.list with no limit returns at most the HTTP default (50), and an explicit limit is capped at the HTTP ceiling (100) — or the divergence is explicitly documented in the plugin API docs with the unbounded behaviour called out.
  • A limit of 0, a negative number, or a non-integer no longer reaches git for-each-ref --count=: it is rejected, defaulted, or normalised to a positive integer before the adapter builds the command.
  • Tests (e.g. in packages/core/test/vcs.test.ts or the plugin host tests) pin: no-limit returns the default page on a repo with more branches than the default, limit: 0 does not return every branch, and a fractional limit does not surface a git usage error.
  • HTTP vcs.branch.list behaviour is unchanged.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    app/corePackage: core (pkgs/ or apps/ in this repo)
    app/pluginPackage: plugin (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Oct 30, 2026
  2. dezren39 commented on Oct 30, 2026

    @dezren39
    Author

    🤖 Agent note · filed

  3. added
    work/assignedAn agent holds this item via the queue (claimed task)
    queue/claimedA queue worker has claimed the task for this item
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 30, 2026
  4. 1 remaining item

  5. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    on Oct 30, 2026
  6. dezren39 commented on Oct 30, 2026

    @dezren39
    Author

    🤖 Agent note · parked — CI pending on PR #946

  7. added
    queue/claimedA queue worker has claimed the task for this item
    and removed
    queue/claimedA queue worker has claimed the task for this item
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 30, 2026
  8. removed
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    on Oct 30, 2026
  9. dezren39 commented on Oct 30, 2026

    @dezren39
    Author

    Completed (task 3782): PR #946 merged (squash, 01cf577ecf) and this issue closed by it. Final CI state at merge: basic typecheck, basic unit (linux), basic unit (windows), basic affected packages, check-standards and check-compliance all SUCCESS. The e2e (linux/windows) jobs never left the queue — stuck QUEUED for ~45 minutes in the fleet runner backlog that also held up main-branch runs — so the merge went ahead per the standing directive after the change itself was verified sound: diff reviewed (limit normalised to default 50 / ceiling 100, non-positive and fractional limits clamped before reaching git for-each-ref), local vcs.test.ts 37 pass with typecheck clean, and the Codex gate clean (0 review threads / 0 reviews on PR #946). If e2e later surfaces a regression from this change, it will arrive as a normal CI failure on main.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/corePackage: core (pkgs/ or apps/ in this repo)app/pluginPackage: plugin (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions