fix(output): output budgets and receipts measure the delivered bytes (GH-1678, GH-1662, pm-sj66lm)

Merged
UN
5d ago
+1661-202

Summary

Output budgets and their receipts now describe the bytes the caller actually receives. This closes three reports in one lineage: GH-1678 (pm-gh1678), GH-1662 (pm-gh1662) and pm-sj66lm. All three sit under the declared-budget feature pm-5t33or.

The shared cause: each receipt measured a representation the CLI did not deliver. Native TOON was measured while a custom renderer printed the output. Compact JSON was measured while the CLI printed pretty JSON or TOON. An intent receipt was estimated before the read_output receipt and a requested row contract joined the envelope.

Changes

GH-1678: custom renderers and output_format services answer to --output-budget

  • withDeliveredOutputRenderer (internal, sdk/read-output-budget.ts) scopes every read-output estimate to the renderer the host delivers: a scoped registerRenderer override, an output_format service string, the calendar markdown default, or lean JSON.
  • A string that cannot fit returns the bounded native output_budget_exceeded refusal with exit 2. A refusal is always rendered and measured natively.
  • A renderer's own nested SDK contract calls measure their own representation, never the outer delivery.
  • deliverContractedRead checks the final custom rendering against every receipt. When a renderer that depends on its own receipts makes a receipt understate the rendering, the read is measured and rendered natively, so no unmeasured byte is delivered. Bounded estimate fixpoints prevent hangs.
  • Commands without a read_output declaration and --output-budget unbounded keep unbounded custom rendering.

GH-1662: one budget contract for intent reads

  • The intent estimate measures the delivered encoding, without a row_contract the caller did not request. It is settled over the complete envelope on every path.
  • A numeric --output-budget binds the intent budget exactly like --token-budget, both through SDK options and through the CLI global flag. The public applyContextIntentProjection signature is unchanged.
  • A settled envelope over its intent budget is decided again with the measured overhead charged, so row compaction leaves room for the receipts. A read is omitted only when it still cannot fit.
  • Every omitted result exits 2, and context_intent, read_output and the omission branch agree. --output-include shaping and continuation-page collapse never strip a refusal.
  • The legacy restore_with carries the 25% recovery margin and the caller's --fields, so the retry it names returns the result.
  • New SDK export isReadResultOmittedForBudget recognizes both omission branches (additive surface change).

pm-sj66lm: TOON explicit selection

  • A complete omission receipt renders as one line, omission_receipt: complete. JSON, SDK and MCP keep the typed receipt.
  • get --fields renders each requested empty collection. pm get <id> --fields id,title,comments drops from 162 B (receipt share 59%) to 105 B (26%).

Gates

  • token-budget-gate.mjs: the infeasible-intent negative control now expects exit 2 and reads the receipt from the failed run.
  • context-intent-calibration.json re-approved. The only change is the 2,243-item session orientation (9,945 → 8,210 tokens, seen items 111 → 83, suppressed repeats unchanged at 8): each session read now fits its own intent budget over its complete envelope.
  • docs/agent-task-token-baseline.json +1 token for the orientation context read.

Review

Six local Codex rounds (gpt-6.1-sol, xhigh) on this branch found 1, 2, 3, 1, 2 and 4 P2s. All were fixed with regressions and negative controls. Details are on pm-gh1678 and pm-gh1662.

Verification

  • tests/unit/core/output/delivered-output-budget.spec.ts and tests/integration/read-output/delivered-output-budget.integration.spec.ts:
    • renderer, service, nested and self-referential renderer cases;
    • the budget-spelling matrix;
    • executed restore_with retries;
    • fit-or-exit-2 budget sweeps;
    • shaped and resumed refusals;
    • exact session charging;
    • TOON selection.
  • Full suite: 12,155 tests passed, exact coverage 100/100/100/100.
  • pnpm quality:static, including the token-budget, calibration, agent-task and defect-evidence gates.
  • SDK surface and contract snapshots.

Docs

docs/READ_OUTPUT_CONTRACTS.md gains the section "Delivered bytes, intent budgets and complete receipts". docs/SDK.md documents renderer and service measurement and isReadResultOmittedForBudget.

Closes #1678
Closes #1662

Summary by Sourcery

Make output budgets and receipts consistently reflect the bytes callers actually receive across renderers, intent reads, and TOON output.

New Features:

  • Support measuring output budgets against bytes delivered by custom renderers and output-format services.
  • Bind numeric --output-budget to intent reads and expose SDK detection for either budget-omission result.
  • Render complete TOON omission receipts compactly while preserving explicitly requested empty collections.

Bug Fixes:

  • Ensure budget receipts, intent projections, refusals, retries, and session charges accurately describe the complete delivered envelope.
  • Make omitted intent results consistently unsuccessful with exit status 2 and preserve refusals through shaping and continuation handling.
  • Prevent custom renderers from delivering bytes that exceed or are not covered by their receipts.

Enhancements:

  • Add bounded handling for self-referential or non-converging renderer estimates and fall back to native rendering when receipts understate delivery.
  • Preserve caller field projections and recovery margins in legacy intent retry commands.

CI:

  • Update token-budget gate expectations and calibration data for unsuccessful intent omissions and revised delivered-byte accounting.

Documentation:

  • Document delivered-byte measurement, intent budgets, complete receipts, renderer constraints, and the new SDK omission helper.

Tests:

  • Add unit and integration coverage for custom renderers, output services, intent budget spellings, retries, shaped and resumed refusals, session charging, and TOON rendering.

Chores:

  • Update the changelog and internal issue tracking records for the three related budget and receipt fixes.