Skip to content

feat(craft): receipt deep links, provider verdicts, and upload coalescing - #14345

Open
nmgarza5 wants to merge 4 commits into
nikg/craft-receipt-recordingfrom
nikg/craft-receipt-extractors
Open

feat(craft): receipt deep links, provider verdicts, and upload coalescing#14345
nmgarza5 wants to merge 4 commits into
nikg/craft-receipt-recordingfrom
nikg/craft-receipt-extractors

Conversation

@nmgarza5

Copy link
Copy Markdown
Contributor

Description

Why. After #14341, a receipt proves an action left the sandbox, but says nothing a user can act on: no link to the thing that was created, a Slack failure inside a 200 still shows CONFIRMED, and one file upload shows as three receipts. This PR makes receipts user-grade proof.

What.

  • New receipt_extractors.py: pure per-provider refinement. Request side names the destination (#exec-team, JSON or form-encoded bodies) and keys the upload flow's final step. Response side builds deep links from shape-checked provider ids (Slack permalink, Drive by mime type, Gmail message), and overrides the verdict when the provider hides failure inside a 200 (Slack ok:false, Linear GraphQL errors). Linear is the one provider whose canonical URL is accepted, behind an anchored shape check. Everything else is constructed from validated ids, never trusted from payloads.
  • Slack's three-request upload flow coalesces into one receipt: step three keys in from its request, step one from its response, and the raw-bytes step through a short-lived session-scoped Redis token map. A later step's failure downgrades the coalesced receipt's early CONFIRMED, and coalesced repeats never re-announce (insert/finalize now report created/changed).
  • The gate buffers a recorded flow's response unless declared oversize and reads it Content-Encoding decoded, with the 1 MiB cap applied to the decoded length.

Example receipt after this PR: slack.messages.write → #exec-team, CONFIRMED, https://slack.com/archives/C0123/p17123456.

How Has This Been Tested?

  • 22 extractor unit tests (links, verdict overrides, coalescing keys, form-encoded bodies, malformed/unsafe input refusal, anchored Linear shapes).
  • 14 gate wiring unit tests, including the gzip decode path, the small-compressed/huge-decoded cap, and chunked-response buffering.
  • Ext-dep tests against real Postgres+Redis: the full three-step upload coalescing to one receipt with exactly two announcements, failure downgrade of a coalesced CONFIRMED, link and verdict persistence, destination-from-request.
  • Full sweeps: tests/unit/sandbox_proxy + external_apps + server 791 passed, craft ext-dep suite 377 passed + 1 xfailed, ty check and ruff clean.

Additional Options

  • This PR should be considered for cherry-picking to the release branch
  • Override Linear Check

@nmgarza5
nmgarza5 requested a review from a team as a code owner August 27, 2026 21:06
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Full-stack Preview (frontend + backend)

Status Preview Commit Updated
https://b8e1ff6-onyx.preview.onyxcorp.dev/ b8e1ff6 2026-08-28 01:44:23 UTC

@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR enriches Craft action receipts with provider-aware destinations, deep links, provider-level verdicts, and Slack upload coalescing while bounding recorded response capture.

  • Adds request and response extractors for Slack, Google Drive, Gmail, and Linear.
  • Coalesces Slack’s multi-request upload sequence into one persisted receipt and announces only meaningful transitions.
  • Streams recorded responses through capped capture and bounded content decoding before refinement.
  • Extends DAL and integration coverage for coalescing, verdict downgrades, links, and response-size boundaries.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
backend/onyx/sandbox_proxy/addons/gate.py Streams every production recorded response through capped capture and performs bounded decoding before receipt refinement.
backend/onyx/sandbox_proxy/receipt_extractors.py Adds provider-specific destination, identifier, deep-link, verdict, and upload-correlation extraction with shape validation.
backend/onyx/sandbox_proxy/receipt_recorder.py Integrates extracted facts into receipt creation, Slack upload coalescing, finalization, and transition announcements.
backend/onyx/server/features/build/db/receipt.py Adds conflict-aware receipt insertion, operation-key assignment, and controlled terminal-state transitions.
backend/tests/external_dependency_unit/craft/test_artifact_receipt_dal.py Exercises database receipt insertion, coalescing, and state-transition behavior.
backend/tests/external_dependency_unit/craft/test_receipt_recording.py Covers end-to-end recording, upload coalescing, persistence, announcements, links, and verdict downgrades.
backend/tests/unit/sandbox_proxy/test_gate_receipts.py Covers gate metadata wiring, capped streaming capture, bounded decoding, and response finalization.
backend/tests/unit/sandbox_proxy/test_receipt_extractors.py Covers provider response shapes, safe links, verdict overrides, form payloads, and Slack upload keys.

Sequence Diagram

sequenceDiagram
    participant Agent
    participant Gate as Sandbox Gate
    participant Provider
    participant Recorder
    participant DB
    participant Redis

    Agent->>Gate: Provider write request
    Gate->>Recorder: Record pending receipt + request facts
    Recorder->>DB: Insert or coalesce receipt
    Recorder-->>Redis: Announce newly created receipt
    Gate->>Provider: Forward approved request
    Provider-->>Gate: Stream response
    Gate->>Gate: Capped capture and bounded decode
    Gate->>Recorder: Finalize with response facts
    Recorder->>DB: Set operation key, link, and verdict
    Recorder-->>Redis: Announce changed receipt
    Gate-->>Agent: Forward provider response
Loading

Reviews (5): Last reviewed commit: "fix(craft): capped observer for every re..." | Re-trigger Greptile

Comment thread backend/onyx/sandbox_proxy/addons/gate.py Outdated
Comment thread backend/onyx/sandbox_proxy/receipt_extractors.py
Comment thread backend/onyx/sandbox_proxy/receipt_extractors.py Outdated
Comment thread backend/tests/unit/sandbox_proxy/test_receipt_extractors.py Outdated
@nmgarza5

Copy link
Copy Markdown
Contributor Author

@greptile

Comment thread backend/onyx/sandbox_proxy/receipt_extractors.py
Comment thread backend/onyx/sandbox_proxy/receipt_extractors.py
The extractors turn transport receipts into user-grade proof. Request-side
facts name the destination (#exec-team from the Slack payload, JSON or
form-encoded) and key the upload flow's final step, response-side facts
build deep links from shape-checked provider ids (Slack permalink, Drive by
mime type, Gmail message), override the verdict when a provider hides
failure inside a 200 (Slack ok=false, Linear errors), and claim
late-learned coalescing keys so the Slack three-request upload lands on one
receipt, the raw-bytes step resolved through a short-lived session-scoped
token map. A later step's failure downgrades the coalesced receipt's early
CONFIRMED, and coalesced repeats never re-announce. Linear's canonical URL
is accepted only through an anchored shape check, everything else is
constructed from validated ids, never trusted. A recorded flow's response
stays buffered unless declared oversize and is read Content-Encoding
decoded, with the cap applied to the decoded length.
An undeclared-length recorded response streams through a capped observer
instead of buffering whole, and decoding is bounded (zlib with a capped
output) so neither a chunked origin nor a compression bomb can grow proxy
memory before the refine cap applies. Google editor APIs link through
their product id keys (documentId, spreadsheetId, presentationId, which
carry no mimeType), and Gmail draft receipts link the drafts view since a
draft is not in #all until sent.
@nmgarza5
nmgarza5 force-pushed the nikg/craft-receipt-extractors branch from c926d44 to fa0ba53 Compare August 27, 2026 21:37
@nmgarza5
nmgarza5 force-pushed the nikg/craft-receipt-recording branch from 09066d4 to 4402909 Compare August 27, 2026 21:37
@nmgarza5

Copy link
Copy Markdown
Contributor Author

@greptile

Comment thread backend/onyx/sandbox_proxy/addons/gate.py Outdated
int() accepts a leading minus, so a negative Content-Length slipped the
refine cap's bounds check and kept the response buffered.
@nmgarza5

Copy link
Copy Markdown
Contributor Author

@greptile

Comment thread backend/onyx/sandbox_proxy/addons/gate.py Outdated
Comment thread backend/onyx/sandbox_proxy/receipt_extractors.py Outdated
…inks

Recorded flows no longer trust the declared length at all: every one
streams through the capped observer, so no origin claim can grow proxy
memory past the refine cap. gmail.drafts.send returns the sent Message,
so it links #all, and only create/update link the drafts view.
@nmgarza5

Copy link
Copy Markdown
Contributor Author

@greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant