Skip to content

[DV-13] [P2] Fit the entire Graft result envelope to the requested byte budget #573

Closed

Description

@wsdt

Priority: P2
Area: Graft context adapter
Repository / target branch: wavect/semaprax / wavect/v090
Reviewed commit: 5428b0805fa3cf35859c843a53e30c3252ef378c
Review date: 6 October 2026

Failure and impact

The Graft adapter's response fitter only removes payload.items. Coverage, skipped-file descriptions, metadata, and diagnostics can already exceed the requested result budget with zero items. In that case the loop cannot shrink anything and the adapter sends the oversized envelope as complete.

The executed case requests a valid 4,096-byte result budget and receives 16,107 bytes, with zero items and 84 skipped files. The host's result parser applies the requested cap to the complete envelope, so this answer is not consumable through the bounded host contract. Mixed-language projects with many unsupported files can lose context responses even though their useful item list is empty or small.

Exact source and cause

Executed reproduction

Run the unchanged adapter through its existing Adapter fixture helper, including child process startup and JSON-RPC request/response handling:

  1. Make a project with the normal helper files and 80 extra unsupported .spx files. Each extra filename contains a modest suffix of 35 characters.
  2. Use the deterministic existing Graft upstream shim; keep its orientation metadata scalar so it satisfies the host's metadata shape.
  3. Initialize the adapter, then issue context.repository/orient with max_items: 10, max_result_bytes: 4096, and full-length project identity digests.
  4. Measure UTF-8 bytes of the complete returned result envelope.
{
  "requested_bytes": 4096,
  "response_bytes": 16107,
  "status": "complete",
  "items": 0,
  "skipped": 84,
  "diagnostics": ["graft.coverage-incomplete"]
}

See repros/context/repro_graft_budget.mjs and evidence/graft-budget-evidence.json. The production adapter and response fitter were executed unchanged. Upstream indexing is a deterministic fixture, not a real installed Graft invocation. The host rejection is established by the cited parser size guard; the end-to-end host/adapter integration was not run in this probe.

Focused fix

Fit the complete serialized result envelope against its effective byte cap. Apply bounded truncation to variable-size non-item fields as well as items, with explicit omission counts and truthful incomplete coverage. Bound diagnostics and metadata before publishing; keep enough information to explain a partial or refused result. Recheck the final envelope after all fields are set.

If the smallest permitted envelope cannot fit, use the protocol's bounded failure path rather than claiming a complete result. Cover early returns that bypass the normal fitter too. Preserve source provenance, deterministic ordering, freshness checks, and the rule that incomplete coverage never proves absence. Reuse a small envelope-aware fitting helper instead of repeating an approximate reserve at each operation.

Acceptance criteria

  • The supplied zero-item/many-skipped-file case fits within 4,096 UTF-8 bytes and remains truthful about omitted coverage.
  • Long paths, multibyte filenames, many diagnostics, adoption metadata, and normal item truncation cannot exceed the final requested cap.
  • All operations and early return paths produce bounded envelopes or an appropriate bounded refusal.
  • Truncation updates omitted counts and prevents false complete/exhaustive/absence claims.
  • A host-level test passes the actual returned bytes through ResultEnvelope::parse_for using the same request cap.
  • Ordinary under-budget results preserve their useful items and provenance without unnecessary truncation.

Existing issue boundary and implementation handoff

#511 (MC-06) repaired the analogous Graphify envelope-budget path. It did not repair Graft's item-only fitter; this issue targets the separate Node adapter owner. Read AGENTS.md and the context/adapter contracts, and extend the existing Graft fixture tests. Report actual envelope byte counts and nonzero test execution, not only payload size.

Activity

  1. wsdt commented on Oct 6, 2026

    @wsdt
    MemberAuthor

    Fixed in 83044ff (pushed to main). New graft/lib/fit.mjs fits the entire serialized envelope (early returns and error paths included), trimming items, skipped-file list, metadata and bounded diagnostics with metadata.omitted_items/skipped_omitted, only ever moving coverage toward incomplete (absence_proven=false when items dropped), and refusing with graft.result-too-large when nothing fits. Evidence: the 4096-byte case produced 16029 bytes before the fix; node budget.test.mjs + 74/74 graft tests pass; graphify_budget dv13 test passes the real adapter output through ResultEnvelope::parse_for at 4096 and 65536.

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