Skip to content

[Proposal] Decide whether request_id_ref is required, and whether a host may derive request_id #305

Open

Description

@rocklambros

Part of issue #118. Raised in the PR #272 review.

The problem

request_id_ref is optional on toolCallResult (specification/v0.1.0/hooks/tool-call-result.json requires only tool, exit_status and outputs), and hooks.md applies the record-it rule only "when present". A host that leaves the field out skips the check completely. A careful attacker leaves it out, and only a careless one gets recorded.

PR #272 makes the reference hosts send it. The shim derives the request's request_id from the host's call id (Claude Code tool_use_id, OpenCode callID) and sends the same value as the result's request_id_ref, so it needs no state. Other implementations have no reason to send it.

That derivation raises its own question. request-envelope.json calls request_id a "unique identifier for this hook invocation", and §17.1's recovery for REPLAY_DETECTED is to regenerate it. A derived id repeats if a host asks twice about one call. PR #272 handles that by retrying once with a random id and giving up the pairing for that call, but the spec says nothing either way.

Decision needed

  1. Make request_id_ref required on toolCallResult in a future version? That's a breaking schema change.
  2. Or keep it optional and require the Guardian to record a result that carries none, as a request_id_ref_absent finding?
  3. Or leave it optional and say plainly that the rule only catches results that carry a reference?
  4. May a host derive request_id from its own call id, as long as each invocation still gets a unique one? Or should the pairing key travel as its own field, so request_id stays random?

Constituencies

Observed Agent implementers, Guardian implementers, auditors and incident responders.

Activity

  1. bar-capsule commented on Oct 28, 2026

    @bar-capsule
    Collaborator
    1. Yes, but later, in a new version or profile. Making it required now breaks the schema.
    2. Yes, for now: keep it optional, and have the Guardian record a result that carries none as a request_id_ref_absent finding, so leaving it out is visible.
    3. Not needed with 2. Recording the absence makes the gap visible instead of only documenting it.
    4. Deriving request_id from the host's call id is fine, as long as every invocation still gets a unique one. I'd keep request_id_ref as the pairing rather than add a separate field. The rule that matters is that it names the request whose decision the host actually applied. The reference adapter gets that wrong after a retry, tracked in [Bug] After a REPLAY_DETECTED retry, a tool result points to the wrong request #351.
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

    status:needs-triageNot yet triaged. Applied by the issue formstype:proposalNew feature or requestworkstream:specOwning workstream. Maintainers only

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions