Skip to content

Approval decision record: bind a verifiable receipt to in-chat approvals #5

@nmrtn

Description

@nmrtn

Problem

In-conversation approval shipped in #4 (v0.1.0/0.1.1): when a payment needs
human approval, pay.stage() returns a staged intent and the MCP server
exposes approve_payment / reject_payment keyed by intent_id. Today the
approval is recorded as a stream of audit events (approval_staged
approval_receivedpayment_completed|payment_denied), not as a single
structured, queryable decision record.

@rpelevin raised this in #4:
"the human said yes in chat" should be a verifiable decision boundary, not a
vague transcript claim. The approval should mint a record that binds exactly
what was approved.

What's bound today

The staged intent + audit chain already carry:

  • intent_id
  • amount, currency
  • recipient_wallet, recipient_url (the target endpoint)
  • intent (the agent's own stated reason for the spend)
  • rule_fired (the policy threshold that was crossed)
  • audit events keyed by intent_id

Proposed: a single ApprovalRecord

Bind, in one structured object returned at approval time and persisted:

  • mcp_server + tool (provenance)
  • intent_id
  • amount, currency, recipient_wallet, recipient_url
  • actor / principal — who initiated; matters once more than one agent or human shares a wallet. New.
  • reason + policy threshold crossed (rule_fired)
  • expires_at — driven by the policy's own timeout_seconds, not a fixed TTL. Overlaps the approval-timeout gap (today the MCP server uses a fixed 1h TTL).
  • params_hash — hash of the exact request (url + method + body + amount) so the approved request == the settled request, closing the approve→settle TOCTOU. New, and worth it.
  • route: allow | revise | human_review | stoprevise (counter-offer a lower cap instead of a binary yes/no) is new.

Open questions

  • Append-only audit JSONL with these fields added, or a separate signed/structured record? blacktea keeps the audit log narrow on purpose; this is the natural place to widen it.
  • revise semantics: a real counter-offer flow, or out of scope for v1?
  • Where does actor/principal come from across the SDK / CLI / MCP surfaces?

Related

Design push credit: @rpelevin.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions