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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
labs/15-pitot/integrations/cursor/beforeShellExecution text eol=lf
180 changes: 157 additions & 23 deletions .github/scripts/pitot_e2e_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,13 @@ def validate_result(
"protocol",
"endpoint",
"prompt_hash",
"nonce",
"receipts",
"hook",
"runtime",
"hooks",
"controller",
"consumer",
"canary",
"commit_sha",
"run_url",
}
Expand All @@ -81,10 +86,10 @@ def validate_result(
raise ValueError("passing result requires real_cli verification")
if value["status"] == "fail" and mode is not None:
raise ValueError("failed result cannot claim a verification mode")
allowed_evidence = {"binary-observed request, accepted response, hook, canary, and final receipts", "real-agent evidence contract failed"}
allowed_evidence = {"binary-observed request, real hook control, projected Consumer, allow/deny canary, and final receipts", "real-agent control evidence contract failed"}
if value["evidence"] not in allowed_evidence:
raise ValueError("invalid evidence summary")
evidence_fields = ("cli", "protocol", "endpoint", "prompt_hash", "receipts", "hook")
evidence_fields = ("cli", "protocol", "endpoint", "prompt_hash", "nonce", "receipts", "runtime", "hooks", "controller", "consumer", "canary")
if value["status"] == "fail":
if any(value[field] is not None for field in evidence_fields):
raise ValueError("failed result cannot carry passing evidence")
Expand All @@ -103,6 +108,9 @@ def validate_result(
raise ValueError("CLI executable digest does not match binary capture")
if value["protocol"] != fixture["dialect"] or not re.fullmatch(r"[0-9a-f]{64}", str(value["prompt_hash"])):
raise ValueError("invalid prompt/protocol receipt")
nonce = value["nonce"]
if not isinstance(nonce, str) or not re.fullmatch(r"[0-9a-f]{32}", nonce):
raise ValueError("invalid session nonce")
expected_endpoint = {
"fixture": f"tests/endpoint-provenance.json#{agent}/{platform}",
"fixture_sha256": fixture["capture_sha256"],
Expand All @@ -114,11 +122,28 @@ def validate_result(
}
if value["endpoint"] != expected_endpoint:
raise ValueError("endpoint receipt does not match pinned real-CLI provenance")
receipt_fields = {"initial_prompt_observed", "tool_call_response_emitted", "tool_result_observed", "final_response_emitted", "hook_observed", "canary_result_observed", "cli_exit_zero"}
receipt_fields = {
"initial_prompt_observed", "allow_tool_call_response_emitted", "allow_tool_result_observed",
"deny_tool_call_response_emitted", "denied_result_observed", "final_response_emitted",
"consumer_observed", "controller_allow_observed", "controller_deny_observed",
"deny_canary_absent", "final_output_observed", "cli_exit_zero",
}
if not isinstance(value["receipts"], dict) or set(value["receipts"]) != receipt_fields or not all(item is True for item in value["receipts"].values()):
raise ValueError("incomplete causal receipts")
if value["hook"] != {"host": agent, "action_kind": "shell", "pitot_exit": 0}:
raise ValueError("invalid Pitot hook receipt")
hooks = value["hooks"]
if not isinstance(hooks, list) or len(hooks) != 2 or [item.get("pitot_exit") for item in hooks] != [0, 2] or any(item.get("host") != agent or item.get("action_kind") != "shell" or item.get("nonce") != nonce for item in hooks):
raise ValueError("invalid Pitot allow/deny hook receipts")
action_ids = [item.get("action_id") for item in hooks]
if len(set(action_ids)) != 2 or value["controller"] != {"id": "e2e-shell-controller", "action_ids": action_ids, "outcomes": ["allow", "deny"]}:
raise ValueError("invalid Controller receipts")
if value["consumer"] != {"id": "e2e-audit", "action_ids": action_ids, "projection": "sha256"}:
raise ValueError("invalid Consumer receipts")
canary = value["canary"]
if not isinstance(canary, dict) or canary.get("denied_executions") != 0 or canary.get("executions") != [f"PITOT_ALLOW {nonce}"]:
raise ValueError("invalid canary execution receipts")
runtime = value["runtime"]
if not isinstance(runtime, dict) or set(runtime) != {"schema_version", "instance_id", "pid", "endpoint", "config_sha256"} or runtime["schema_version"] != 1:
raise ValueError("invalid authenticated runtime receipt")
if not isinstance(value["commit_sha"], str) or not re.fullmatch(r"[0-9a-f]{40}", value["commit_sha"]):
raise ValueError("invalid commit_sha")
if not isinstance(value["run_url"], str) or not re.fullmatch(
Expand All @@ -141,6 +166,42 @@ def validate_inventory(value: object) -> dict[str, object]:
return value


def validate_runtime_result(
value: object, *, platform: str, expected_sha: str | None = None, expected_run_url: str | None = None,
) -> dict[str, object]:
required = {"schema_version", "capability", "platform", "status", "verification_mode", "evidence", "nonce", "runtime", "controller", "receipts", "commit_sha", "run_url"}
if not isinstance(value, dict) or set(value) != required:
raise ValueError("runtime result fields do not match schema")
if value["schema_version"] != 2 or value["capability"] != "explicit_request" or value["platform"] != platform:
raise ValueError("runtime result identity does not match artifact")
if value["status"] not in {"pass", "fail"}:
raise ValueError("invalid runtime result status")
if value["status"] == "pass":
if value["verification_mode"] != "real_runtime" or value["evidence"] != "real request CLI, authenticated runtime, and correlated allow/deny Controller receipts":
raise ValueError("passing runtime result lacks real evidence")
if not isinstance(value["nonce"], str) or not re.fullmatch(r"[0-9a-f]{32}", value["nonce"]):
raise ValueError("invalid runtime session nonce")
controller = value["controller"]
if not isinstance(controller, dict) or controller.get("outcomes") != ["allow", "deny"] or not isinstance(controller.get("action_ids"), list) or len(controller["action_ids"]) != 2:
raise ValueError("runtime Controller receipt is incomplete")
if not isinstance(value["receipts"], dict) or not all(item is True for item in value["receipts"].values()):
raise ValueError("runtime causal receipts are incomplete")
runtime = value["runtime"]
if not isinstance(runtime, dict) or set(runtime) != {"schema_version", "instance_id", "pid", "endpoint", "config_sha256"} or runtime["schema_version"] != 1:
raise ValueError("runtime identity receipt is invalid")
elif value["verification_mode"] is not None or any(value[field] is not None for field in ("nonce", "runtime", "controller", "receipts")):
raise ValueError("failed runtime result cannot carry passing evidence")
if not isinstance(value["commit_sha"], str) or not re.fullmatch(r"[0-9a-f]{40}", value["commit_sha"]):
raise ValueError("invalid runtime commit_sha")
if not isinstance(value["run_url"], str) or not re.fullmatch(r"https://github\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/actions/runs/[0-9]+", value["run_url"]):
raise ValueError("invalid runtime run_url")
if expected_sha is not None and value["commit_sha"] != expected_sha:
raise ValueError("runtime result commit does not match workflow run")
if expected_run_url is not None and value["run_url"] != expected_run_url:
raise ValueError("runtime result URL does not match workflow run")
return value


def aggregate(
platform_results: dict[str, dict[str, object] | None],
platforms: tuple[str, ...] = PLATFORMS,
Expand Down Expand Up @@ -168,13 +229,18 @@ def failed_result(agent: str, platform: str, head_sha: str, run_url: str) -> dic
"platform": platform,
"status": "fail",
"verification_mode": None,
"evidence": "real-agent evidence contract failed",
"evidence": "real-agent control evidence contract failed",
"cli": None,
"protocol": None,
"endpoint": None,
"prompt_hash": None,
"nonce": None,
"receipts": None,
"hook": None,
"runtime": None,
"hooks": None,
"controller": None,
"consumer": None,
"canary": None,
"commit_sha": head_sha,
"run_url": run_url,
}
Expand All @@ -186,6 +252,7 @@ def render_comment(
head_sha: str,
agent_records: tuple[dict[str, str], ...] | None = None,
platforms: tuple[str, ...] = PLATFORMS,
runtime_results: dict[str, dict[str, object] | None] | None = None,
) -> str:
lines = [
MARKER,
Expand All @@ -211,6 +278,23 @@ def render_comment(
f"{result_cell(platform_results.get('macos'))} | {result_cell(platform_results.get('windows'))} | "
f"{icons[status]} | {evidence} |"
)
lines.extend([
"",
"### Runtime capabilities",
"",
"| Capability | Ubuntu | macOS | Windows | Result |",
"|---|---|---|---|---|",
])
capability_results = runtime_results or {}
runtime_status = aggregate(capability_results, platforms)
def runtime_cell(result: dict[str, object] | None) -> str:
if result is None:
return "⏳ Pending"
return "✅ Pass · real runtime allow/deny" if result["status"] == "pass" else "❌ Failed"
lines.append(
f"| `pitot request` | {runtime_cell(capability_results.get('ubuntu'))} | "
f"{runtime_cell(capability_results.get('macos'))} | {runtime_cell(capability_results.get('windows'))} | {icons[runtime_status]} |"
)
lines.extend(["", f"Source commit: `{head_sha[:12]}`", ""])
return "\n".join(lines)

Expand Down Expand Up @@ -293,6 +377,18 @@ def load_inventory(github: GitHub, artifact: dict[str, object]) -> dict[str, obj
return validate_inventory(json.loads(bundle.read(names[0])))


def load_runtime_artifact(
github: GitHub, artifact: dict[str, object], *, platform: str, expected_sha: str, expected_run_url: str,
) -> dict[str, object]:
archive = github.download(str(artifact["archive_download_url"]))
with zipfile.ZipFile(io.BytesIO(archive)) as bundle:
names = bundle.namelist()
if names != ["runtime-result.json"] or bundle.getinfo(names[0]).file_size > MAX_ARTIFACT_BYTES:
raise ValueError("runtime artifact must contain one bounded runtime-result.json")
value = json.loads(bundle.read(names[0]))
return validate_runtime_result(value, platform=platform, expected_sha=expected_sha, expected_run_url=expected_run_url)


def collect_results(
github: GitHub,
by_name: dict[str, dict[str, object]],
Expand Down Expand Up @@ -323,32 +419,69 @@ def collect_results(
return results


def failed_runtime_result(platform: str, head_sha: str, run_url: str) -> dict[str, object]:
return {
"schema_version": 2, "capability": "explicit_request", "platform": platform,
"status": "fail", "verification_mode": None, "evidence": "explicit request evidence contract failed",
"nonce": None, "runtime": None, "controller": None, "receipts": None,
"commit_sha": head_sha, "run_url": run_url,
}


def collect_runtime_results(
github: GitHub, by_name: dict[str, dict[str, object]], platforms: tuple[str, ...], head_sha: str, run_url: str,
) -> dict[str, dict[str, object]]:
results: dict[str, dict[str, object]] = {}
for platform in platforms:
artifact = by_name.get(f"pitot-e2e-runtime-{platform}")
if artifact is not None:
try:
results[platform] = load_runtime_artifact(
github, artifact, platform=platform, expected_sha=head_sha, expected_run_url=run_url,
)
continue
except (ValueError, json.JSONDecodeError, zipfile.BadZipFile):
pass
results[platform] = failed_runtime_result(platform, head_sha, run_url)
return results


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--event", type=Path, required=True)
parser.add_argument("--repository", required=True)
args = parser.parse_args()
event = json.loads(args.event.read_text(encoding="utf-8"))
workflow_run = event["workflow_run"]
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise SystemExit("GITHUB_TOKEN is required")
github = GitHub(args.repository, token)
pull_requests = workflow_run.get("pull_requests", [])
if not pull_requests:
owner = workflow_run.get("head_repository", {}).get("owner", {}).get("login")
branch = workflow_run.get("head_branch")
if owner and branch:
head = urllib.parse.quote(f"{owner}:{branch}", safe=":")
candidates = github.request("GET", f"/pulls?state=open&head={head}&per_page=20")
pull_requests = [pr for pr in candidates if pr.get("head", {}).get("sha") == workflow_run["head_sha"]]
if "pull_request" in event:
pull_requests = [event["pull_request"]]
head_sha = event["pull_request"]["head"]["sha"]
run_id = int(os.environ["GITHUB_RUN_ID"])
server_url = os.environ.get("GITHUB_SERVER_URL", "https://github.com")
run_url = f"{server_url}/{args.repository}/actions/runs/{run_id}"
elif "workflow_run" in event:
workflow_run = event["workflow_run"]
pull_requests = workflow_run.get("pull_requests", [])
if not pull_requests:
print("No pull request is associated with this workflow run; nothing to report.")
return 0
head_sha = workflow_run["head_sha"]
run_url = workflow_run["html_url"]
owner = workflow_run.get("head_repository", {}).get("owner", {}).get("login")
branch = workflow_run.get("head_branch")
if owner and branch:
head = urllib.parse.quote(f"{owner}:{branch}", safe=":")
candidates = github.request("GET", f"/pulls?state=open&head={head}&per_page=20")
pull_requests = [pr for pr in candidates if pr.get("head", {}).get("sha") == workflow_run["head_sha"]]
if not pull_requests:
print("No pull request is associated with this workflow run; nothing to report.")
return 0
head_sha = workflow_run["head_sha"]
run_url = workflow_run["html_url"]
run_id = int(workflow_run["id"])
else:
raise ValueError("reporter requires a pull_request or workflow_run event")

artifacts = github.list_run_artifacts(int(workflow_run["id"]))
artifacts = github.list_run_artifacts(run_id)
by_name = {artifact["name"]: artifact for artifact in artifacts if not artifact.get("expired")}
inventory_artifact = by_name.get("pitot-e2e-inventory")
if inventory_artifact is None:
Expand All @@ -359,8 +492,9 @@ def main() -> int:
platforms = tuple(platform["id"] for platform in inventory["platforms"])
run_urls = {agent: run_url for agent in agent_ids}
results = collect_results(github, by_name, inventory, head_sha, run_url)
runtime_results = collect_runtime_results(github, by_name, platforms, head_sha, run_url)

body = render_comment(results, run_urls, head_sha, agent_records, platforms)
body = render_comment(results, run_urls, head_sha, agent_records, platforms, runtime_results)
issue_number = pull_requests[0]["number"]
comments = github.request("GET", f"/issues/{issue_number}/comments?per_page=100")
existing = next(
Expand Down
30 changes: 0 additions & 30 deletions .github/workflows/pitot-e2e-report.yml

This file was deleted.

Loading
Loading