Skip to content

[Design] Extend the pageview paging window to the MCP task-lens verbs (for / explore) #294

Description

@pt-act

Problem

pageview.h is the one paging window and truncation vocabulary, and the high-cardinality CLI
row-listing verbs use it — shown= / total= / capped= / has_more= / next_offset=, fully resumable.
Derivation: pageWindow( is called from 25 source files / 68 call sites outside its own definition
header (rg -c 'pageWindow\(' src | grep -v '^src/pageview.h:'; files and call sites are one-command
countable, so that is the figure stated rather than an approximate verb count).

The MCP task-lens verbs (for, explore) do not. They cut by byte/token budget mid-stream
(handoff.h:291,386 drops heuristic rows tail-first; verbs_for.h:333-345 cuts at the adaptiveCut
relevance cliff; packtask.h:1690 emits bodies grouped by FILE, stopping on a BYTE budget). They
expose capped="1" but no continuation handle, so an agent that overflows the budget cannot fetch the
remainder without inventing a follow-up call it was never handed. This is the "complete-last-page" gap
pageview.h rule 4/H8 already names for row verbs, still open on the task-lens verbs.

Proposed direction — no new vocabulary

Reuse pageWindow(total, limit, offset) (pageview.h:129) over each verb's ranked candidate set, and
reuse the existing bounded limit/offset argument path (mcpverbs.h:327-333) — only the
advertisement of those two arguments on for/explore is new. The continuation is a CANDIDATE offset
(into the ranked order total= already counts), never a byte offset — bodies are byte-trimmed and
file-grouped, so a byte cursor is meaningless. Page k = pageWindow(candidateTotal, limit, offset)
→ the existing byte packer over that slice → the existing pageDisclosure quintet. Additive:
arguments only, no new verb, no new attribute, no new parser.

Out of scope, deliberately: batch (an offset on its 16-sub-query envelope is ill-defined; a
paginating sub-query should call its verb directly) and fixed small blocks (situ.h records why an
offset on a fixed block dropped rows silently — the fix there was deleting the cap, not paging it).

The one decision I'd like maintainer input on

adaptiveCut computes a relevance cliff over the candidate set. How should it compose with paging?

Proposed: compute the cliff ONCE over the full ranked candidate set. Page 1 = the above-cliff tier
(byte-identical for non-paginating callers — that stays the regression floor). Continuation pages serve
below-cliff candidates in raw rank order, carrying their own disclosed set marker (e.g.
tier="below-cliff") so a consumer never reads cliff-rejected content as relevant. total= counts the
full set once; the cliff is a boundary label within it.

Alternative (named, not recommended): re-run adaptiveCut per page. Rejected because it fractures
total= (a different implied denominator per page) and re-opens the "two dialects reporting different
sets under one count" defect class packtask.h:1690 already paid to close.

Blessing or overturning this is the gate to the upstream PR.

Status

A gate-first PoC is in progress on our fork (branch + CI, not our main): arms written first into the
existing gates — truncvocabcheck (paged root carries the full quintet), listingpagingcheck
(offset=limit continuation seam, no gap/overlap), handoffcheck (within-page byte trim keeps rule-5
disclosure), budgetpolicycheck (per-page budget holds), packtaskcheck (below-cliff pages carry
their own marker), adaptivecutshapecheck (cliff invariance) — each shown red on a planted defect
before code. The arm asserting cliff invariance encodes the proposed answer above and will be rewritten
if maintainers pick the alternative; nothing assumes its own open question. Measured evidence will be
linked here when the branch's CI is green. Same discipline as #291.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions