Surfaced during Rock's review of PR #22 ("Guardian rejection and Guardian failure are the same thing to the adapter").
The gap
§6.4 defines a decision failure as "no usable decision arrives" — timeout, transport failure, or "the Guardian returns an error instead of a decision" — and says all three resolve to the on_decision_failure posture (default proceed).
But a JSON-RPC error response is not the same event as a silent Guardian. SIGNATURE_INVALID (-32004), REPLAY_DETECTED (-32005), TIMESTAMP_OUT_OF_WINDOW (-32006), and Invalid Request (-32600, e.g. an oversized envelope rejected before policy runs) mean the Guardian is alive and refused this envelope — and every one of them is attacker-reachable from inside the agent:
- Oversize a tool argument past the Guardian's body cap → -32600 before any policy code runs → fail-open posture proceeds → the write executes ungoverned.
- Replay a request_id the adapter derives deterministically (uuid5 of the framework's tool_use_id) → -32005 → same bypass.
- Strip or corrupt the envelope signature → -32004 → same bypass.
Under the default fail-open posture, §6.4-as-written converts each refusal into a policy-bypass primitive.
Proposed direction
Distinguish two classes in §6.4:
- Decision failure (no usable answer: timeout, transport failure, 5xx) →
on_decision_failure posture, as today.
- Guardian refusal (a §17.1 error meaning alive-and-refused: -32000, -32004, -32005, -32006, -32600, -32700) → the Observed Agent MUST treat as DENY regardless of posture, and MUST record an audit event.
Reference-adapter status
The PR #22 reference adapters already implement class 2 (fail closed on refusals, guardian_refusal_fail_closed audit events) as defense-in-depth ahead of the spec text — a deployment choosing a stricter per-cause posture is within its discretion. This issue tracks making the distinction normative so conformance tests can assert it.
References
Surfaced during Rock's review of PR #22 ("Guardian rejection and Guardian failure are the same thing to the adapter").
The gap
§6.4 defines a decision failure as "no usable decision arrives" — timeout, transport failure, or "the Guardian returns an error instead of a decision" — and says all three resolve to the
on_decision_failureposture (defaultproceed).But a JSON-RPC error response is not the same event as a silent Guardian.
SIGNATURE_INVALID(-32004),REPLAY_DETECTED(-32005),TIMESTAMP_OUT_OF_WINDOW(-32006), and Invalid Request (-32600, e.g. an oversized envelope rejected before policy runs) mean the Guardian is alive and refused this envelope — and every one of them is attacker-reachable from inside the agent:Under the default fail-open posture, §6.4-as-written converts each refusal into a policy-bypass primitive.
Proposed direction
Distinguish two classes in §6.4:
on_decision_failureposture, as today.Reference-adapter status
The PR #22 reference adapters already implement class 2 (fail closed on refusals,
guardian_refusal_fail_closedaudit events) as defense-in-depth ahead of the spec text — a deployment choosing a stricter per-cause posture is within its discretion. This issue tracks making the distinction normative so conformance tests can assert it.References
docs/spec/instrument/specification.md§6.4, §17.1