Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions live/guild/app/preflight.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ def run(url: str, *, store=None) -> dict[str, Any]:
and evidence == "protocol_handshake"
and protocol_probe.get("protocol") == "mcp"
and protocol_probe.get("result") == "proven")
http_only = (status == "http_responsive"
and evidence == "http_response"
and protocol_probe.get("protocol") == "unspecified"
and protocol_probe.get("result") == "not_attempted")
if status == "recently_reachable" and evidence == "protocol_handshake":
checks.append(_check("endpoint_reachable", "proven",
rec.get("detail") or "responded"))
Expand Down Expand Up @@ -184,14 +188,18 @@ def run(url: str, *, store=None) -> dict[str, Any]:
checks.append(_check("agent_card_signed",
"proven" if signed else "failed", why))
else:
# MCP does not require an A2A Agent Card. Keep its absence unknown;
# a served malformed/unsigned card still receives the existing checks.
optional_absent_card = mcp_proven and code == 404
# Neither observed MCP nor an HTTP-only probe requires an A2A card.
# Only a 404 is optional absence; existing card defects stay visible.
optional_absent_card = code == 404 and (mcp_proven or http_only)
card_detail = err or f"no parsable card (http {code})"
if optional_absent_card:
card_detail = (
"no A2A card (http 404); not required for the observed MCP handshake"
if mcp_proven else
"no A2A card (http 404); no A2A protocol probe applies to this HTTP endpoint")
checks.append(_check("agent_card_resolves",
"unknown" if optional_absent_card else "failed",
"no A2A card (http 404); not required for the "
"observed MCP handshake" if optional_absent_card
else err or f"no parsable card (http {code})"))
card_detail))
checks.append(_check("agent_card_signed", "unknown",
"not attempted — no card to inspect"))

Expand Down
48 changes: 48 additions & 0 deletions live/guild/tests/test_preflight.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import json
import os
import socket
import sys

import pytest
Expand Down Expand Up @@ -87,6 +88,53 @@ def test_http_200_without_a_handshake_is_do_not_delegate(monkeypatch):
assert "protocol_handshake" in out["failed"]


@pytest.mark.parametrize("http_code", [200, 402, 405])
def test_plain_http_api_does_not_require_an_a2a_card(monkeypatch, http_code):
"""Coppice's /api/vet responded, but an optional card 404 caused caution."""
monkeypatch.setattr(reachability, "_resolve_and_screen", lambda *args:
(True, [(socket.AF_INET, "93.184.216.34")], "ok"))
monkeypatch.setattr(reachability, "_http_request_pinned",
lambda *args, **kw: (http_code, b""))
monkeypatch.setattr(preflight, "_probe_get", lambda *args: (404, b"", ""))
out = preflight.run("https://example.com/api/vet")
assert out["verdict"] == "no_failed_checks"
assert out["failed"] == []
assert out["scored"] == ["endpoint_reachable"]
assert set(out["unknowns"]) == {
"protocol_handshake", "agent_card_resolves", "agent_card_signed",
"payment_claim_holds", "independent_evidence"}
assert "not an endorsement" in out["headline"]


@pytest.mark.parametrize("card_code, card_body", [
(200, b'{"name":"Example"}'), (200, b'not a card'), (403, b''),
])
def test_plain_http_api_still_reports_card_defects(monkeypatch, card_code, card_body):
monkeypatch.setattr(reachability, "_resolve_and_screen", lambda *args:
(True, [(socket.AF_INET, "93.184.216.34")], "ok"))
monkeypatch.setattr(reachability, "_http_request_pinned",
lambda *args, **kw: (200, b""))
monkeypatch.setattr(preflight, "_probe_get",
lambda *args: (card_code, card_body, ""))
out = preflight.run("https://example.com/api/vet")
assert out["verdict"] == "delegate_with_caution"
assert out["failed"]
assert "protocol_handshake" in out["unknowns"]
assert "payment_claim_holds" in out["unknowns"]


def test_plain_http_api_failure_is_still_blocking(monkeypatch):
monkeypatch.setattr(reachability, "_resolve_and_screen", lambda *args:
(True, [(socket.AF_INET, "93.184.216.34")], "ok"))
monkeypatch.setattr(reachability, "_http_request_pinned",
lambda *args, **kw: (503, b""))
monkeypatch.setattr(preflight, "_probe_get", lambda *args: (404, b"", ""))
out = preflight.run("https://example.com/api/vet")
assert out["verdict"] == "do_not_delegate"
assert "endpoint_reachable" in out["failed"]
assert "protocol_handshake" in out["unknowns"]


def test_unreachable_reports_downstream_checks_as_unknown_not_failed(monkeypatch):
_fake(monkeypatch, probe={"status": "currently_unreachable",
"evidence_level": "none"}, card_code=0)
Expand Down
Loading