Skip to content

[Bug] ASK has no way to deliver the approver's answer to the Guardian #175

Open

Description

@President

What is broken

ASK pauses a step until an approver answers. The approver answers to the Observed Agent. The spec does not say how that answer gets back to the Guardian.

Where

specification/v0.1.0/ask-details.json, and §6 and §9 of docs/spec/instrument/specification.md

What the specification says, and what happens instead

ask-details.json requires approver, question and timeout_seconds, and offers options. So the Guardian can say:

  • who should answer
  • what is being asked
  • which answers are acceptable

Nothing says how the answer comes back. There is no method for it, and no field to put it in. timeout_seconds and timeout_disposition only cover the case where nobody answers.

In practice the agent can send the step again, with the chosen option somewhere in the payload. The Guardian binds it to the ASK it raised and answers allow or deny. I tested this on top of the Go port (#87) and it works.

The spec gives no name to the field that carries the chosen option. So it only works between one agent and one Guardian.

#14 is the mirror of this on DEFER. There the Guardian side decides, and the agent is the one left without a way to learn the answer.

Impact on implementers

Two implementations that both support ASK cannot work together on it. Each picks its own name for the answer. An agent built against one Guardian will not resolve an ASK raised by another.

ASK is one of the five dispositions §6 makes mandatory, so this affects every conformant pair.

Current Priority Scope

This is deferred or out of scope and I am filing it to be tracked

Activity

  1. imran-siddique commented on Oct 19, 2026

    @imran-siddique

    When the answer reaches the Guardian through the Observed Agent, as in your Go port test, the only signature on it is the agent's per-session HMAC (§10). Then "Guardian MUST verify approver identity against policy" (§9, specification.md:317 at 9c05f04) has nothing to check. The answer field should name the ASK's step_id and the request_id it resolves, carry a digest of the approved parameters, and be signed by the approver.

  2. President commented on Oct 19, 2026

    @President
    Author

    @imran-siddique - agree: the answer must point to the exact ASK and the exact action, and be signed by the approver.

    My suggestion:

    1. ASK from the Guardian - as in the spec today, only options becomes required (the Guardian needs a fixed list to map each answer to allow or deny):

    "result": {
      "request_id": "6f1c...",
      "decision": "ask",
      "ask_details": {
        "approver": { "type": "human", "id": "alice@example.com" },
        "question": "Delete table orders?",
        "options": ["approve_once", "deny"],   // NEW: required (optional today)
        "timeout_seconds": 300
      }
    }

    2. The agent repeats the same step with a new request_id and the answer:

    "params": {
      "request_id": "9a2d...",
      "payload": { ...same as in the first step... },
      "ask_resolution": {                      // NEW
        "ask_request_id": "6f1c...",           // NEW: the step that got the ASK
        "selected_option": "approve_once",     // NEW: one of ask_details.options
        "signature": { ... }                   // NEW, optional: signed by the approver
      },
      "signature": { ... }                     // as today: signed by the agent
    }

    The Guardian checks the answer against step 6f1c...: that step got an ASK in this session, the payload is the same as in that step, and the option is one of its options. Otherwise it denies. The Guardian already keeps that step, so the answer needs no digest of the action.

    The approver's signature proves that a real approver answered, not the agent. I'd make it optional for now: today the Guardian trusts the agent and all it's data anyway - an agent that could fake the answer can also just not call the Guardian.

    What do you think?

    P.S. Your digest fits another case: an approval the agent gets before any ASK, signed by the approver - then there is no stored step, so the digest of the action must be inside the signed part. But it is a separate topic..

  3. imran-siddique commented on Oct 21, 2026

    @imran-siddique

    Pointing at the stored step works, and for this path it makes the digest unnecessary. Agreed on options becoming required.

    I would keep the approver signature required. An agent that skips the Guardian leaves a hole in the hash chain that an auditor can see. An agent that fills in selected_option itself leaves an entry that reads as alice@example.com approving the delete. One is a missing record, the other is a false one.

    If it has to stay optional for 0.x, then without it the Guardian should log the resolution as agent-asserted rather than as the approver's, and §9's "MUST verify approver identity" applies only when the signature is present. That is the same point #272 adds to §8.6 for HMAC decisions.

  4. President commented on Oct 27, 2026

    @President
    Author

    @imran-siddique - agree, the approver’s signature should be required, BUT only when approvers have a way to sign. The spec doesn’t define one yet, so for now the field can only be optional.

    Without the approver’s signature, the Guardian records the answer as “declared” (the agent could say so), not “verified”.

    How approvers sign is a separate topic for the spec.

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:bugSomething isn't working

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions