Skip to content

feat: output budget — --fields projection, --limit/--cursor pagination, truncated/next_cursor envelope #131

Description

@rshade

Overview

Add a bounded-output layer that mirrors the bounded 1 MiB input cap (spec 001):
--fields field projection, --limit/--cursor pagination, and envelope-level
truncated: true / next_cursor signaling for list commands. Unbounded list
output otherwise lands directly in an agent's context window, defeating the
determinism and predictability guarantees the library exists to protect.

User Story

As an LLM agent driving an ax-go CLI, I want list commands to project only the
fields I ask for and page results under an explicit limit, So that a large
result set cannot blow my context window and I can resume iteration with a
stable cursor instead of re-fetching everything.

Problem Statement

ax-go caps input at 1 MiB at the read boundary (spec 001,
config/ DefaultMaxBytes), but places no ceiling on output. A list command
over a large collection emits every row, every field, in one payload — the
bounded-input discipline has no bounded-output counterpart. Agents pay for
every token; an unbounded list is both a cost and a correctness hazard (the
tail may be silently dropped by a downstream buffer with no truncated signal).
This is P2 because cursor semantics across heterogeneous commands need a real
design pass (opaque vs. field-based cursors, stability under concurrent
mutation, determinism of the cursor token itself).

Proposed Solution

Introduce an additive output-budget layer in the contract packages plus opt-in
persistent flags, framed as complementary to the existing NDJSON streaming and
output-determinism harness (spec 006):

Technical Approach

  • Add three optional persistent flags alongside the existing agent-safety set:
    --fields (comma-separated projection allowlist), --limit (max items),
    --cursor (opaque continuation token). Register them the same way
    --format/--dry-run/--idempotency-key are registered, via
    internal/cli constants + EnsurePersistentStringFlag/Int.
  • Define an additive list-result envelope in contract/: either a dedicated
    ListEnvelope[T] wrapping Envelope[T] with Truncated bool +
    NextCursor string (omitempty), or a Page *PageInfo sub-struct on the
    envelope. Chosen shape is a Spec Kit design decision; both stay additive and
    additive-tolerant (Constitution XI) — existing non-list envelopes are
    unaffected.
  • Provide a projection helper (contract-side) that filters marshaled output to
    the --fields allowlist deterministically (stable key order), so projection
    never introduces non-determinism.
  • Cursor semantics are the hard part and get their own research pass: token must
    be opaque, deterministic for identical inputs, and documented as
    non-deterministic only where it legitimately encodes position state.
  • Surface the new flags in __schema automatically (they flow through
    internal/schema flag reflection) — this is where Richer per-flag __schema semantics (defaults/enums/examples) #28's richer per-flag
    semantics would describe them.

Files Likely Affected

  • internal/cli/cli.go - new flag-name constants + EnsurePersistentIntFlag
  • execute.go - register the new persistent flags in prepareCommand
  • contract/json.go - additive list/pagination envelope shape + projection helper
  • contract/context.go - context carriers for resolved limit/cursor/fields (optional)
  • json.go - root-package re-exports of any new contract symbols
  • schema/schema.go / internal/schema/schema.go - flag reflection surfaces the new flags
  • examples/integration/main.go - demonstrate a paginated/projected list command (the stream command at streamCommandName is the model)
  • internal/testutil - determinism harness coverage for the paginated path

Acceptance Criteria

  • --fields, --limit, --cursor are recognized persistent flags surfaced in __schema
  • A list command exceeding --limit emits truncated: true and a non-empty next_cursor; passing that cursor back returns the next page deterministically
  • --fields projects output to exactly the requested keys with stable ordering
  • Envelope changes are additive: existing Envelope[T] output is byte-identical for non-list commands
  • Tests pass with adequate coverage (per AGENTS.md testing-first discipline)
  • Documentation updated if needed

Out of Scope

  • Sorting/filtering DSLs (only projection + limit + cursor)
  • Server-side/database pagination adapters — the library defines the envelope + flag contract, not storage integration
  • Retrofitting pagination onto NDJSON streaming (streaming is inherently unbounded-by-design; this targets bounded list envelopes)

Technical Notes

New envelope fields and flag/schema metadata live in the CONTRACT packages
(contract/, schema/), never the runtime facade, so thin consumers pin the
shape without compiling the telemetry stack. Everything is additive and
additive-tolerant (Constitution XI) — a new flag/field is additive; renaming or
retyping an existing one would be breaking (not proposed here). Output stays
byte-identical deterministic; the cursor token, if position-encoding, must be
documented as a non-deterministic field. Routes through the Spec Kit workflow
(issue → spec → plan → tasks); the cursor-semantics design is the core research
question and should not be pre-decided in this issue.

Testing Strategy

  • Unit tests for --fields projection (key allowlist, stable order) and --limit truncation boundary
  • Golden-file tests for the list envelope with truncated/next_cursor and for the __schema output gaining the three flags
  • Round-trip test: next_cursor fed back as --cursor yields the contiguous next page
  • Determinism harness (spec 006 / internal/testutil) coverage on the paginated + projected path

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/largeLarge effort - multi-dayenhancementNew feature or requestroadmap/futureFuture milestones - Future Vision

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions