Skip to content

VC-824: Keep ticket list under the CLI response limit and name an oversized answer - #934

Merged
hussainph merged 4 commits into
mainfrom
volli/VC-824-volli-ticket-list-will-fail-at-1-350-tickets-the
Oct 19, 2026
Merged

hussainph merged 4 commits into
mainfrom
volli/VC-824-volli-ticket-list-will-fail-at-1-350-tickets-the

Conversation

@hussainph

@hussainph hussainph commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #929 (VC-778 crash-recovery fixture fix, head 637c7d1, test-only). Its head is merged into this branch additively (merge commit 935009c), because the VC-778 flake kept failing Test (packages) here. Once #929 merges, the diff against main is this PR's own change.

Problem

volli ticket list sends every non-archived ticket with its full markdown body (read-verbs.ts, ticketListVerb). The CLI stops reading any answer over 4 MiB (client.ts, MAX_RESPONSE_BYTES). On a long-lived board (about 1,350 tickets at this board's average size, fewer when bodies are long), the command exits 1 with SOCKET_PROTOCOL: The app response is too large. The recovery it prints is "confirm the CLI bundle and running app come from a compatible build", which sends people looking for a version mismatch that doesn't exist. The human table never prints a body, but it was still sent all of them.

Fix

  • ticket.list accepts body: false and leaves body out of each row. Every other field stays. Any other non-boolean value is INVALID_REQUEST.
  • The human table sends body: false (run.ts), so its answer is about a sixth of the size. The table it prints is byte-identical.
  • --json keeps full rows by default. New opt-in flag --no-body drops the bodies.
  • An answer over the limit is now RESPONSE_TOO_LARGE (new error code, exit 1), and the message names the verb. For ticket list, the recovery comes from the verb's registry entry (overflowNext). It leads with --status and --limit, then mentions --no-body, which only helps with a matching app build, and volli ticket show <id>. Verbs without an entry get the generic "narrow the request with the verb's filters" guidance. The build-compatibility hint is never shown for this case.
  • The 4 MiB client limit itself is unchanged.
  • volli help changes has a new VC-824 entry recording the flag, the new code, the table change and the fix.

Decisions

  • Additive, opt-in contract. Default --json rows keep body, so existing consumers are not moved. Only the human table, which never showed bodies, stops asking for them. I did not add a server-side byte bound with an omitted count: it would silently truncate the default --json answer. Instead, an oversized --json answer fails with guidance, as the ticket's acceptance allows.
  • No bodyLength or excerpt field on summary rows. It isn't needed to fix this, and it would be one more field to freeze. ticket show reads a body.
  • N−1: an older app ignores body and answers full rows. The table renders the same as before, with the old size behaviour. An older CLI sends no body and gets today's rows. The app never sends RESPONSE_TOO_LARGE, because only the CLI produces it. pnpm check:protocol-schema --base origin/main passes, since ticket.list is CLI-only and has no published schema.
  • Out of scope: volli board also returns bodies in its rows. With this change, its overflow reports RESPONSE_TOO_LARGE with the generic guidance instead of the build hint, but it still carries the bodies.

Tests

All run single-file with --maxWorkers=1 through the machine's serial runner:

  • packages/host-core/src/agent-dispatch/ticket-list.test.ts (new): drives the real dispatch on 2,000 tickets with 3 KB bodies. With body: false the frame is under 4 MiB and has all 2,000 rows and no bodies. Default rows keep body, and that frame is over 4 MiB. A non-boolean body is refused. Before the fix it failed (6,557,827-byte frame, and the bad selector was accepted).
  • packages/cli/src/run.test.ts (new block): runs runCli over a real Unix socket against a server answering with the same 2,000-ticket board. volli ticket list exits 0 and prints exactly the table the full rows would render. --json --no-body exits 0 with bodiless rows. --json exits 1 with RESPONSE_TOO_LARGE, the verb named, and --status/--limit/--no-body guidance, and no SOCKET_PROTOCOL or compatible-build text. Before the fix the table case failed with error[SOCKET_PROTOCOL] … compatible build.
  • packages/cli/src/client.test.ts: the oversize case now expects RESPONSE_TOO_LARGE with the verb named. A generic verb gets the default guidance, and ticket.list gets its registry overflowNext.
  • packages/cli: parser.test.ts, help.test.ts, and cli-reference.test.ts (snapshot regenerated: the new flag, the new error code, the changes entry).
  • packages/shared: agent-product.test.ts (the newest-entry pin moved to VC-824), agent-surface.test.ts, harness/skill-content.test.ts, verb-registry.test.ts.
  • vp check on all touched files; pnpm check:protocol-schema --base origin/main.

Review

Changes from the review of 935009c, in commit 1bc0f54:

  • Overflow advice order (SHOULD 1), done. The ticket list advice now leads with --status / --limit, which shrink the answer from any app build. It words --no-body as "with a matching app build", because an older app ignores body. The capability record says so too.
  • Advice in the registry (SHOULD 2), done. Added an optional overflowNext field on VerbEntry. ticket.list declares it, and the socket client reads it through verbEntry(request.cmd). The client no longer has a per-verb branch, so board can declare its own advice later.
  • One limit constant (nit), done. AGENT_RESPONSE_LIMIT_BYTES and AGENT_RESPONSE_LIMIT_LABEL are exported from @volli/shared. The client guard, the refusal message, the RESPONSE_TOO_LARGE why text, the help-changes entry and ticket-list.test.ts all use them. The literal "4 MiB" left in client.test.ts/run.test.ts is the expected output those tests check.
  • Board built once (nit), done. ticket-list.test.ts builds the 2,000-ticket board once in beforeAll. The body-selector refusal runs on an empty board.
  • Older-app case (nit), done. run.test.ts has a new case where an older app ignores body: false on a 20-ticket board. The table is identical to the one rendered from bodiless rows.

CI reruns

  • Run 39242282176, attempt 1: Test (packages) failed only in session-runtime/crash-recovery.integration.test.ts ("reconciles an accepted command with a different persisted timestamp…" and "abandons a pending flat ask durably…", both expected 'rejected' to be 'accepted'). That is the known flake VC-778, and this diff doesn't touch session runtime. I reran the failed jobs once.
  • Attempt 2: Test (packages) failed only in the same VC-778 test ("reconciles an accepted command…", expected { closed: 1, errors: [ … ] } to deeply equal { closed: 0, errors: [] }). The fix for that flake is VC-778: Bound only deliberately stalled stages in the crash-recovery fixture #929, still open and test-only. I reran the failed jobs once more.
  • Attempt 3: VC-778 passed. Test (packages) failed only in worktree/watch.test.ts ("500 Done tickets merged in an earlier launch and 10 open PRs cost 10 gh calls a cycle"). That is the known timeout flake VC-856, unrelated to this diff. VC-856 was outside my allowed rerun list. After the supervising Session approved it, I reran the failed jobs once (attempt 4).
  • Attempt 4: VC-856 passed. Test (packages) failed only in the VC-778 test again ("reconciles an accepted command…", { closed: 1, errors: [ … ] }), its third failure in four attempts. I have not rerun it again. The plan is to merge origin/main once VC-778: Bound only deliberately stalled stages in the crash-recovery fixture #929 (the VC-778 fix) lands, then re-run.
  • After attempt 4, by owner direction: no more reruns. I merged VC-778: Bound only deliberately stalled stages in the crash-recovery fixture #929's head into the branch and pushed, which started a fresh CI run.
  • After commit 1bc0f54 (review follow-ups): fresh CI run on push, no reruns.

…very fixture (VC-778)

The fixture set followUpAdmissionTimeoutMs: 50 for the whole runtime, but
that bound covers every adapter.attach (workspace + executor stages) and
every cold binding rebuild, not only queued messages. A slow but healthy
real Pi attach under CI load then came back rejected with
queue_admission_timeout. Genuine work now keeps the production 30 s bound;
the 50 ms bound applies only once the fixture enters an attach/admission
gate. Adds a regression test with a real slow attach, and receipts in the
accepted-status assertion messages.
…rsized answer

The ticket list table asked for every ticket body, which it never prints;
on a long-lived board that answer passes the CLI's 4 MiB limit and failed
as SOCKET_PROTOCOL with a build-compatibility hint.

- ticket.list accepts body: false and omits body from each row; the human
  table sends it, --json keeps full rows unless --no-body is passed.
- An answer past the limit is RESPONSE_TOO_LARGE, naming the verb; for
  ticket list it suggests --status, --limit, --no-body and ticket show.
- Record both in the capability changes (volli help changes).
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 30677d9d-343a-47d0-a4ce-13df496e9c9d
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…limit constant

- Lead ticket list's overflow advice with --status/--limit, which shrink the
  answer from any app build; --no-body only helps with a matching app.
- The advice lives on the verb's registry entry (overflowNext), read by the
  socket client through verbEntry, not a per-verb branch in the client.
- AGENT_RESPONSE_LIMIT_BYTES/_LABEL in @volli/shared: the client guard, its
  refusal, the error guidance and the help-changes record share one value.
- ticket-list.test builds the 2,000-ticket board once; run.test covers an
  older app that ignores body: false.
@hussainph
hussainph merged commit c764763 into main Oct 19, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant